android 波浪进度WaveView

刚好有注意到百度外卖以及淘宝个人中心,都用到了类似水波起伏的效果,于是就参照网上的资料然后自己整改,自定义了一个waveView ,原理么,首先就是自定义个WaveView 继承View,然后再WaveView 内部实现代码逻辑:
① 水波就波嘛? sin函数? 贝塞尔曲线? 都行,这里就用二阶贝塞 尔曲线去画吧
② 波要动嘛,怎么动呢?线程? 好吧 这里用了个Handler。
③绘制波首先要找点,那么在onMeasure()里找出需要的点咯,这里就暂时展示一个波段吧,一个波长移动左边不就没了?OK 那就两个波吧,吼吼,两个波(猥琐男潜质表露无遗啊)。接下来就是Handler 结合 onDraw()绘制。OK,那就先看我Word绘制的粗瘪的波动图,请看VCR,oh,no... gif

wave.gif

意思就是波平移一个波长之后回到初始位置继续平移循环。
好吧,有人说了,这么简单的逻辑你要啰嗦那么多???
好吧,我承认,我有唐僧的潜质。。。
闲话就不说了,先上

效果图

waveprogress.gif

调用的Activity

 * Created by LiuDong on 2016/12/22.
 * Email:15002102128@126.com
 */

public class WaveActivity extends Activity {
    LD_WaveView waveView;//方形
    LD_WaveView waveCircleView;//圆形
    private int progrees=0;//进度
    private Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (progrees==100) progrees=0;
            Log.i("progress",progrees+"");
            waveView.setmProgress(progrees++);
            waveCircleView.setmProgress(progrees++);
            mHandler.sendEmptyMessageDelayed(0,100);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wave);
        waveView= (LD_WaveView) findViewById(R.id.waveView);
        waveCircleView= (LD_WaveView) findViewById(R.id.waveViewCircle);
        mHandler.sendEmptyMessageDelayed(0,10);
    }
}

xml布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical" android:layout_width="match_parent"
    android:background="@color/ld_White"
    android:layout_height="match_parent">
    <com.dadong.ld_tools.widget.LD_WaveView
        android:id="@+id/waveViewCircle"
        android:layout_marginTop="20dp"
        android:layout_width="100dp"
        android:layout_centerHorizontal="true"
        android:layout_height="100dp"
        app:wave_color="@color/ld_Black"
        app:wave_circle="true"
        />
    <com.dadong.ld_tools.widget.LD_WaveView
        android:id="@+id/waveView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:wave_color="@color/ld_Black"
        app:wave_circle="false"
        android:layout_centerInParent="true" />
</RelativeLayout>

自定义WaveView

/**
 * Created by LiuDong on 2016/12/23.
 * Email:15002102128@126.com
 */

public class LD_WaveView extends View {

    private int mProgress;//进度
    private int mTimeStep = 10;//时间间隔
    private int mSpeed = 5;//波单次移动的距离
    private int mViewHeight;//视图宽高
    private int mViewWidth;//视图宽度
    private int mLevelLine;// 基准线

   
    private int mWaveLength;//波长 暂定view宽度为一个波长
    private int mStrokeWidth;//园的线宽
    private RectF rectF;//圆环区域
    private int mWaveHeight;//波峰高度
    private int mLeftWaveMoveLength;//波平移的距离,用来控制波的起点位置
    private int mWaveColor;//波的颜色
    private Paint mPaint;//画笔
    private Paint mCirclePaint;//圆环画笔
    private Paint mBorderPaint;//边界画笔
    private int   mBorderWidth=4;//边界宽度
    private Paint mTextPaint;//文字画笔
    private Path mPath;//绘画线
    private List<Point> mPoints;//点的集合
    private boolean isMeasure = false;//是否已测量过
    private boolean isCircle=false;//是否圆形默认false,可属性代码设置
    //处理消息
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

            initWaveMove();
        }
    };

    /**
     * 初始化波的移动
     */
    private void  initWaveMove(){
        mLeftWaveMoveLength+=mSpeed;//波向右移动距离增加mSpeed;
        if (mLeftWaveMoveLength>=mWaveLength){//当增加到一个波长时回复到0
            mLeftWaveMoveLength=0;
        }
        invalidate();

    }
    public LD_WaveView(Context context) {
        this(context, null);
    }

    public LD_WaveView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LD_WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        getAttr(context, attrs, defStyleAttr);
        init();

    }

    /**
     * 初始化画笔
     */
    private void init() {
        mPoints = new ArrayList<Point>();
        //波浪轨迹画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mWaveColor);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);


        mPath = new Path();


        //文字画笔
        mTextPaint=new Paint();
        mTextPaint.setColor(Color.RED);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(48);


        //圆环画笔
        mCirclePaint=new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setColor(Color.WHITE);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        //边界线画笔
        mBorderPaint=new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mWaveColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBorderPaint.setStyle(Paint.Style.STROKE);


    }

    /**
     * 获取自定义的属性值
     *
     * @param attrs
     */
    private void getAttr(Context context, AttributeSet attrs, int defStyle) {

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LD_WaveView, defStyle, 0);

        mWaveColor = a.getColor(R.styleable.LD_WaveView_wave_color, Color.RED);
        isCircle=a.getBoolean(R.styleable.LD_WaveView_wave_circle,false);
        a.recycle();

    }


    /**
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!isMeasure&&Math.abs(getMeasuredHeight()-getMeasuredWidth())<50) {//只计算一次就够了 ,relativelayout的时候要绘制两次 加个宽高判断
            mViewHeight = getMeasuredHeight();
            mViewWidth = getMeasuredWidth();
            mLevelLine = mViewHeight;  //初始化波的准位线       起始位视图最底部
            {
                mLevelLine = mViewHeight * (100-mProgress) / 100;
                if (mLevelLine < 0) mLevelLine = 0;
            }
            //计算波峰值
            mWaveHeight = mViewHeight / 20;//波峰暂定为view高度的1/20,如果需要设置 可设置set方法赋值;
            mWaveLength = getMeasuredWidth();

            //计算所有的点 这里取宽度为整个波长  往左再延伸一个波长 两个波长则需要9个点
            for (int i = 0; i < 9; i++) {
                int y = 0;
                switch (i % 4) {
                    case 0:
                        y = mViewHeight;
                        break;
                    case 1:
                        y =mViewHeight+ mWaveHeight;
                        break;
                    case 2:
                        y = mViewHeight;
                        break;
                    case 3:
                        y = mViewHeight-mWaveHeight;
                        break;
                }
                Point point = new Point(-mWaveLength + i * mWaveLength / 4, y);
                mPoints.add(point);
            }
            /**
             * 计算圆环宽度
             */
            int mIncircleRadius=mViewHeight<mViewWidth?mViewHeight/2:mViewWidth/2;//内切圆半径

            int mcircumcircleRadius= (int) (Math.sqrt((float)(Math.pow(mViewHeight/2,2)+Math.pow(mViewWidth/2,2)))+0.5);//外接圆半径
            int radius=mcircumcircleRadius/2+mIncircleRadius/2;

            rectF=new RectF(mViewWidth/2-radius,mViewHeight/2-radius,mViewWidth/2+radius,mViewHeight/2+radius);
            mStrokeWidth=mcircumcircleRadius-mIncircleRadius;
            mCirclePaint.setStrokeWidth(mStrokeWidth);//线是有宽度的  采用了这种方式画圆环
            isMeasure = true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * 绘制线条
         */
        mPath.reset();
        int i = 0;
        mPath.moveTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mPoints.get(0).getY()-mViewHeight*mProgress/100);
        for (; i < mPoints.size() - 2; i += 2) {
            mPath.quadTo(mPoints.get(i + 1).getX()+mLeftWaveMoveLength, mPoints.get(i + 1).getY()-mViewHeight*mProgress/100, mPoints.get(i + 2).getX()+mLeftWaveMoveLength, mPoints.get(i + 2).getY()-mViewHeight*mProgress/100);
        }
        mPath.lineTo(mPoints.get(i).getX()+mLeftWaveMoveLength, mViewHeight);
        mPath.lineTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mViewHeight);
        mPath.close();
        /**
         * 绘制轨迹
         */
        canvas.drawPath(mPath,mPaint);
        Rect rect = new Rect();

        String progress=String.format("%d%%",mProgress);
        mTextPaint.getTextBounds(progress,0,progress.length(), rect);
        int textHeight = rect.height();
        if (mProgress>=50)//如果进度达到50 颜色变为白色,没办法啊,进度在中间 不变颜色看不到
            mTextPaint.setColor(Color.WHITE);
        else
        mTextPaint.setColor(mWaveColor);
        canvas.drawText(progress,0,progress.length(),mViewWidth/2,mViewHeight/2+textHeight/2,mTextPaint);

        if (isCircle) {
            /**
             * 绘制圆环
             */

            canvas.drawArc(rectF, 0, 360, true, mCirclePaint);
            Paint circlePaint = new Paint();
            circlePaint.setStrokeWidth(5);
            circlePaint.setColor(Color.WHITE);
            circlePaint.setAntiAlias(true);
            circlePaint.setStyle(Paint.Style.STROKE);
            canvas.drawCircle(mViewWidth / 2, mViewHeight / 2, mViewHeight / 2, circlePaint);
            /**
             * 绘制边界
             */

            mBorderPaint.setStrokeWidth(mBorderWidth/2);
        canvas.drawCircle(mViewWidth/2,mViewHeight/2,mViewHeight/2-mBorderWidth/2,mBorderPaint);
        }else {
            /**
             * 绘制矩形边框
             */
            canvas.drawRect(0,0,mViewWidth,mViewHeight,mBorderPaint);
        }
        //
        handler.sendEmptyMessageDelayed(0,mTimeStep);
    }

    /**
     * 设置进度  基准线
     * @param mProgress
     */
    public void setmProgress(int mProgress) {
        this.mProgress = mProgress;
        mLevelLine=(100-mProgress)*mViewHeight/100;
    }

    /**
     * 设置是否为圆形
     * @param circle
     */
    public void setCircle(boolean circle) {
        isCircle = circle;
    }
}

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LD_WaveView">
        <attr name="wave_color" format="color"></attr>
        <attr name="wave_circle" format="boolean"></attr>
    </declare-styleable>
</resources>
OK,没了,代码里备注应该还算比较清楚了,希望能对一些人有一些帮助,瑕疵不足之处欢迎指正,或者有好的建议。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 文/LD_大东(简书作者)原文链接:http://www.jianshu.com/p/2384ccba5dc3著作...
    sirai阅读 490评论 0 1
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,401评论 25 708
  • 一说起经典,大家的脑子里第一时间浮现的肯定是诸如《巴黎圣母院》、《百年孤独》之类难以下咽的鸿篇巨著。 这样经典的作...
    楚游尘阅读 929评论 0 50
  • 周围总是有人跟我诉说TA的心事,觉得我是个善解人意的知心朋友,可以在对方脆弱伤心时恰到好处的安慰和鼓励,可是过...
    小小太阳万丈光芒阅读 702评论 1 0
  • 从那天 连绵的笑声 重重撞开了深锁的门 从那天 迅急的心跳 深深震撼了梦里的魂 从那天 汹涌的血流 悄悄裹挟了安分...
    灵山阅读 325评论 0 1