Android 圆形波浪控件

参考网络上的代码参考并加以修改,写成了以下一个控件。

先上图(视频没处理好,让圆形少了个底...):

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);

猜你喜欢

转载自blog.csdn.net/u012218652/article/details/104777310