参考网络上的代码参考并加以修改,写成了以下一个控件。
先上图(视频没处理好,让圆形少了个底...):
onDraw里代码量少,核心代码就是使用正弦函数计算波浪的x、y坐标。
我这里波浪所处的高度是固定的,波浪的轴在原中间。这个修改也很容易,只要调整setPath方法的yTrans参数就可以。
分享出来以供大家参考。
public class WaveCircleView extends View {
/**
* 波浪画笔
*/
private Paint mWavePaint;
/**
* 波浪路径
*/
private Path mWavePath;
/**
* 圆环画笔
*/
private Paint mCirclePaint;
/**
* 圆环路径
*/
private Path mCirclePath;
/**
* 波的颜色
*/
private int mWaveColor = Color.parseColor("#449FFF");
private int mBackColor = Color.parseColor("#1285FF");
private int mViewHeight;
private int mViewWidth;
private float mWaveLength;
/**
* 波浪x轴平移的距离
*/
private float mWaveXtrans = 0;
private float mSpeed;
private MyHandler mHandler = new MyHandler(this);
/**
* 初始化波的移动
*/
private void initWaveMove(){
// 移动距离增加mSpeed;
mWaveXtrans += mSpeed;
if (mWaveXtrans >= mWaveLength){
// 超过宽度时
mWaveXtrans = 0;
}
invalidate();
mHandler.removeCallbacksAndMessages(null);
mHandler.sendEmptyMessageDelayed(0, 50);
}
public NetworkDetectCircle(Context context) {
this(context, null);
}
public NetworkDetectCircle(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public NetworkDetectCircle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewHeight = getMeasuredHeight();
mViewWidth = getMeasuredWidth();
mWaveLength = mViewWidth / 2F;
mSpeed = mViewWidth / 800F;
int circleRadius = mViewHeight < mViewWidth ? mViewHeight / 2 : mViewWidth / 2;
mCirclePath.addCircle(mViewWidth / 2, mViewHeight / 2, circleRadius, Path.Direction.CCW);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制背后的圆
canvas.drawPath(mCirclePath, mCirclePaint);
// 在裁切的圆上画波浪
canvas.save();
canvas.clipPath(mCirclePath);
setPath(mWavePath, mViewWidth, mViewHeight, mViewHeight / 20F, mWaveLength,
mWaveXtrans, mViewHeight / 2F);
canvas.drawPath(mWavePath, mWavePaint);
canvas.restore();
}
private void init() {
// 波浪画笔
mWavePaint = new Paint();
mWavePaint.setAntiAlias(true);
mWavePaint.setColor(mWaveColor);
mWavePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mWavePath = new Path();
// 圆画笔
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(mBackColor);
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePath = new Path();
mHandler.sendEmptyMessage(0);
}
/**
* 设置波浪路径
* @param path 路径
* @param width 控件宽度
* @param height 控件高度
* @param waveHeight 波浪的高度
* @param waveLength 波长
* @param xTrans x轴平移长度
* @param yTrans y轴平移长度
*/
private void setPath(Path path, int width, int height, float waveHeight, float waveLength, float xTrans, float yTrans) {
int x;
int y;
// 每次进来都把path重置一下
path.reset();
for (int i = 0; i < width; i++) {
x = i;
y = (int) (waveHeight * Math.sin((i / waveLength) * Math.PI * 2 + xTrans) + yTrans);
if (i == 0) {
// x=0的时候,即左上角的点,移动画笔于此
path.moveTo(x, y);
}
// 用每个x求得每个y,用quadTo方法连接成一条贝塞尔曲线
path.quadTo(x, y, x + 1, y);
}
//最后连接到右下角底部
path.lineTo(width, height);
//连接到左下角底部
path.lineTo(0, height);
//起点在左上角,这个时候可以封闭路径了,封闭。
path.close();
}
private static class MyHandler extends Handler {
WeakReference<WaveCircleView> mReference;
MyHandler(WaveCircleView waveCircleView) {
mReference = new WeakReference<>(waveCircleView);
}
@Override
public void handleMessage(Message msg) {
if (mReference.get() != null) {
mReference.get().initWaveMove();
}
}
}
}
后续优化:
控件中的Handler主要作用是调用View的invalidate()方法。代码中通过handler发送时间和增加speed横向距离来控制速度。这样来控制速度就需要调整两个变量。通过speed变量来计算每次onDraw时应该移动的距离是不错的方式,代码如下:
if (mXMoveTimeLastTime > 0L) {
float duration = System.currentTimeMillis() - mXMoveTimeLastTime;
float dx = mSpeed * duration / 1000;
// 移动距离增加dx
mWaveXtrans += dx;
if (mWaveXtrans >= mWaveLength) {
// 超过宽度时
mWaveXtrans = 0;
}
}
mXMoveTimeLastTime = System.currentTimeMillis();
mMyHandler.sendEmptyMessageDelayed(0, 50);