參考網絡上的代碼參考並加以修改,寫成了以下一個控件。
先上圖(視頻沒處理好,讓圓形少了個底...):
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);