贝塞尔曲线画的WaveView,波浪头的效果

最近发现很多App都使用了这种效果,没办法就自己网上搞一波资料,也自己写一个,先看效果:


把它放在 一个头部下面,着实比平平的放着好看些许.......




受上博客启发,下载代码后,修改了下,变成了双向的贝塞尔曲线波,左右两边移动,就形成了如上图所示的效果

废话不多说,说一下大概的画法流程:

这样,两个波相对的走,就会引起上图的效果.

/**
 *
 * @author   清风
 *
 */
public class WaveView extends View {

    private float mViewWidth;
    private int mViewHeight;
    private float mLevelLine;
    private float mWaveHeight = 20;
    private float mWaveWidth = 200;

    private float mLeftSide;
    private float mRightSide;
    private float mMoveLen;
    private float mMoveLen1;
    /**
     * 水波平移速度
     */
    public static final float SPEED = 0.8f;
    public static final float SPEEDX = 1.2f;
    public static final int SPEEDWAVE = 3;   //时间速度   越小越快

    private List<Point> mPointsList;
    private List<Point> mPointsList1;
    private Paint mPaint;
    private Paint mPaint1;
    private Path mWavePath;
    private Path mWavePath1;
    private boolean isMeasured = false;
    private Timer timer;
    private MyTimerTask mTask;

    private void resetPoints(){
        mLeftSide = -mWaveWidth;
        for (int i = 0; i < mPointsList.size(); i++){
            mPointsList.get(i).setX(i * mWaveWidth / 4 - mWaveWidth);
        }
    }
    private void resetPoints1(){
        mRightSide = mWaveWidth*2;
        for (int i = 0; i < mPointsList.size(); i++){
            mPointsList1.get(i).setX(i * mWaveWidth / 4);
        }
    }
    public WaveView(Context context){
        super(context);
        init();
    }

    public WaveView(Context context, AttributeSet attrs){
        super(context, attrs);
        init();
    }

    public WaveView(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        mPointsList = new ArrayList<>();
        mPointsList1= new ArrayList<>();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.parseColor("#ffffff"));  //白色

        mPaint1 = new Paint();
        mPaint1.setAntiAlias(true);
        mPaint1.setStyle(Paint.Style.FILL);
        mPaint1.setColor(Color.parseColor("#DFF2FF")); //蓝白色

        mWavePath = new Path();
        mWavePath1 = new Path();
    }

    public void startWaveLine(){
        if (mTask != null){
            mTask.cancel();
            mTask = null;
        }
        if (timer!=null){
            timer.cancel();
            timer = null;
        }
        timer = new Timer();
        mTask = new MyTimerTask(updateHandler);
        timer.schedule(mTask, 0, SPEEDWAVE);
    }

    public void stopWaveLine(){
        if (mTask != null){
            mTask.cancel();
            mTask = null;
        }
        if (timer!=null){
            timer.cancel();
            timer = null;
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!isMeasured) {
            isMeasured = true;

            mViewHeight = getMeasuredHeight();

            mViewWidth = getMeasuredWidth()/4.0f;

            mLevelLine = mViewHeight / 2;
            // 根据View宽度计算波形峰值
            mWaveHeight = mViewWidth / 10.0f;
            // 波长等于四倍View宽度也就是View中只能看到四分之一个波形,这样可以使起伏更明显
            mWaveWidth = mViewWidth * 4;
            // 左边隐藏的距离预留一个波形
            mLeftSide = -mWaveWidth;
            mRightSide = mWaveWidth*2;
            // 这里计算在可见的View宽度中能容纳几个波形,注意n上取整
            int n = (int) Math.round(mViewWidth / mWaveWidth + 0.5);
            // n个波形需要4n+1个点,但是我们要预留一个波形在左边隐藏区域,所以需要4n+5个点
            for (int i = 0; i < (4 * n + 5); i++){
                // 从P0开始初始化到P4n+4,总共4n+5个点
                float x = i * mWaveWidth / 4 - mWaveWidth;
                float y = 0;
                switch (i % 4){
                    case 0:
                    case 2:
                        // 零点位中线上
                        y = mLevelLine;
                        break;
                    case 1:
                        // 往下波动的控制点
                        y = mLevelLine + mWaveHeight;
                        break;
                    case 3:
                        // 往上波动的控制点
                        y = mLevelLine - mWaveHeight;
                        break;
                }
                mPointsList.add(new Point(x, y));
                mPointsList1.add(new Point(x, y));
            }
            int h=1;
            for (int t=1;t<mPointsList1.size();t=t+2){
                if (h%2==1){
                    mPointsList1.get(t).setY(mPointsList.get(t).getY()-mWaveHeight*2);
                }else {
                    mPointsList1.get(t).setY(mPointsList.get(t).getY()+mWaveHeight*2);
                }
                h++;
            }
            for (int x=0;x<mPointsList.size();x++){
                mPointsList1.get(x).setX(mPointsList.get(x).getX()+mWaveWidth);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas){
        mWavePath1.reset();

        int j =  5;
        mWavePath1.moveTo(mPointsList1.get(8).getX(), mPointsList1.get(8).getY());
        for (; j >= -1; j = j - 2){
            mWavePath1.quadTo(mPointsList1.get(j + 2).getX(),mPointsList1.get(j + 2).getY(), mPointsList1.get(j + 1).getX(), mPointsList1.get(j + 1).getY());
        }
        mWavePath1.lineTo(mPointsList.get(0).getX(), mViewHeight);
        mWavePath1.lineTo(mRightSide, mViewHeight);
        mWavePath1.close();
        canvas.drawPath(mWavePath1, mPaint1);


        mWavePath.reset();
        int i = 0;
        mWavePath.moveTo(mPointsList.get(0).getX(), mPointsList.get(0).getY());
        for (; i < mPointsList.size() - 2; i = i + 2) {
            mWavePath.quadTo(mPointsList.get(i + 1).getX(),mPointsList.get(i + 1).getY(), mPointsList.get(i + 2).getX(), mPointsList.get(i + 2).getY());
        }
        mWavePath.lineTo(mPointsList.get(8).getX(), mViewHeight);
        mWavePath.lineTo(mLeftSide, mViewHeight);
        mWavePath.close();
        // mPaint的Style是FILL,会填充整个Path区域
        canvas.drawPath(mWavePath, mPaint);
    }

    class MyTimerTask extends TimerTask{
        Handler handler;

        public MyTimerTask(Handler handler)
        {
            this.handler = handler;
        }

        @Override
        public void run()
        {
            handler.sendMessage(handler.obtainMessage());
        }

    }
    Handler updateHandler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            // 记录平移总位移
            mMoveLen += SPEED;
            mMoveLen1+=SPEEDX;

            if (mLevelLine < mViewHeight/2)
                mLevelLine = mViewHeight/2;
            mLeftSide += SPEED;
            mRightSide -= SPEEDX;
            // 波形平移
            for (int i = 0; i < mPointsList.size(); i++){
                mPointsList.get(i).setX(mPointsList.get(i).getX() + SPEED);
            }
            for (int i = 0; i < mPointsList1.size(); i++){
                mPointsList1.get(i).setX(mPointsList1.get(i).getX() - SPEEDX);
            }
            if (mMoveLen >= mWaveWidth){
                // 波形平移超过一个完整波形后复位
                mMoveLen = 0;
                resetPoints();
            }
            if (mMoveLen1 >= mWaveWidth) {
                // 波形平移超过一个完整波形后复位
                mMoveLen1 = 0;
                resetPoints1();
            }
            invalidate();
        }
    };
    class Point {
        private float x;
        private float y;

        public float getX()
        {
            return x;
        }

        public void setX(float x)
        {
            this.x = x;
        }

        public float getY()
        {
            return y;
        }

        public void setY(float y)
        {
            this.y = y;
        }

        public Point(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

    }

}

附上源码下载地址:




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s清风s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值