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,没了,代码里备注应该还算比较清楚了,希望能对一些人有一些帮助,瑕疵不足之处欢迎指正,或者有好的建议。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,192评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,186评论 1 303
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,844评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,471评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,876评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,891评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,068评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,791评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,539评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,772评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,250评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,577评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,244评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,146评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,949评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,995评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,812评论 2 276

推荐阅读更多精彩内容

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