android自定义view 模仿win10进度条

来源:互联网 发布:微信运动的数据来源 编辑:程序博客网 时间:2024/05/16 01:55

android自定义view 模仿win10进度条

本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发。


PS:有朋友反映动画无法播放,那是因为PathMeasure.getLength()只在KITKAT以上版本有效,而对于以下版本一定要关闭硬件加速才有效。

先上预览图:

预览图

流程

  • 1.一个匀速圆周运动的点
  • 2.多个匀速圆周运动的点
  • 3.多个圆周运动的点,速度由快到慢
  • 4.点与点之间的间距线性减少,动画的最后合为一个点
  • 5.为了让动画看起来更加流畅,需要在动画即将结束的时候手动绘制点

核心控件

  • PathMeasure:截取Path中的一部分并显示
  • ValueAnimator:完成动画从初始值平滑的过度到结束值的效果,同时还负责管理动画的播放次数、播放模式、以及对动画设置监听器等

流程一

思路

  1. 先用path画一个圆
  2. ValueAnimator设置为0f-1f的平滑
  3. 用PathMeasure根据ValueAnimator返回的值截取path上的一个点
    private Paint mPaint;    private Path mPath;    private PathMeasure mPathMeasure;    private int mWidth,mHeight;    private ValueAnimator valueAnimator;    //用这个来接受ValueAnimator的返回值,代表整个动画的进度    private float t;
  • 初始化画笔
    mPaint = new Paint();    mPaint.setStyle(Paint.Style.STROKE);    mPaint.setStrokeWidth(15);    mPaint.setColor(Color.WHITE);    //设置画笔为园笔    mPaint.setStrokeCap(Paint.Cap.ROUND);    //抗锯齿    mPaint.setAntiAlias(true);
  • 初始化Path和mPathMeasure
    这里角度不能选360,否则会测量失误,具体原因和android的内部优化有关
    mPath = new Path();    RectF rect = new RectF(-150,-150,150,150);    mPath.addArc(rect,-90,359.9f);    mPathMeasure = new PathMeasure(mPath,false);
  • 初始化ValueAnimator
        valueAnimator = ValueAnimator.ofFloat(0f,1f).setDuration(3000);        valueAnimator.setRepeatCount(-1);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                t = (float) animation.getAnimatedValue();                invalidate();            }        });

这里的ValueAnimator设置的是一个时长3秒的动画,再这3秒中,ValueAnimator会返回一个由0f-1f平滑的数字
ValueAnimator.ofFloat(0f,1f).setDuration(3000)
在这里我们用t来接受返回值,同时刷新视图

t = (float) animation.getAnimatedValue();invalidate();

这里写图片描述
这里可以看出t的值,有0到1,这里就可以把t理解为我们这个动画的进度

  • 开始绘制
    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.translate(mWidth/2,mHeight/2);        Path dst = new Path();        mPathMeasure.getSegment(mPathMeasure.getLength()*t,mPathMeasure.getLength()*t+1,dst,true);        canvas.drawPath(dst,mPaint);    }
  • 结果图:
    这里写图片描述

流程二

思路

  • 我们设置让t每间隔0.05就画一个点,总共画4个点,注意这里getSegment()的最后一个要设置为true来保证画出来的是多个点而不是一条线
    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.translate(mWidth/2,mHeight/2);        Path dst = new Path();        int num = (int) (t/0.05);        float s,y,x;        switch(num){            default:            case 3:                x = t-0.15f;                s = mPathMeasure.getLength();                y = s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 2:                x = t-0.10f;                s = mPathMeasure.getLength();                y = s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 1:                x = t-0.05f;                s = mPathMeasure.getLength();                y = s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 0:                x = t;                s = mPathMeasure.getLength();                y = s*x;                mPathMeasure.getSegment(y,y+1,dst,true);                break;        }        canvas.drawPath(dst,mPaint);    }
  • 结果图
    这里写图片描述

流程三

思路

我们先绘制出路程-时间的函数图像
这里写图片描述
函数为y = -x*x + 2*x,当x=1时,y=mPathMeasure.getLength();
设s = mPathMeasure.getLength();
最终我们套用函数:y = -s*x*x+2*s*x;
这里的Y轴代表的是path的长度,X轴对应时间
所以把流程二中的y = s*x改成y = -s*x*x+2*s*x即可

switch(num){            default:            case 3:                x = t-0.15f;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 2:                x = t-0.10f;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 1:                x = t-0.05f;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 0:                x = t;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);                break;        }        canvas.drawPath(dst,mPaint);
  • 结果图
    这里写图片描述

流程四

思路

虽然流程3中点与点的间距已经开始减少,不过这只是因为速度不同间距才改变的,我们的目的是让这些点到最后合并为1个点,也就是说开始的时候每个点的X间距0.05,结束的时候要让他们的X相同
目前点之间X的间距函数如下:
这里写图片描述
我们最后要让当X=1时,他们的Y值相等,而且他们X的间距由0.05线性平滑到0
这里写图片描述
看函数图像已经很清楚了,修改后代码如下:

        switch(num){            default:            case 3:                x = t-0.15f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 2:                x = t-0.10f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 1:                x = t-0.05f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 0:                x = t;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);                break;        }
  • 结果图
    这里写图片描述

流程五

思路

这里已经完成了99.9%了,但细心的同学会发现,进度条每次转动一圈聚成一个点后都会闪一下,这是因为重新开始动画刷新视图的原因,这里的补救方法就是我们在动画快结束的时候手动画一个点

        if(t>=0.95){            canvas.drawPoint(0,-150,mPaint);        }

这样我们就完成了这个进度条
这里写图片描述
最后这里附上源码:

package com.example.zhangml.view;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PathMeasure;import android.graphics.RectF;import android.util.AttributeSet;import android.view.View;/** * Created by zhangml on 2016/9/12 0012. */public class Win8Search extends View{    private Paint mPaint;    private Path mPath;    private PathMeasure mPathMeasure;    private int mWidth,mHeight;    private ValueAnimator valueAnimator;    //用这个来接受ValueAnimator的返回值,代表整个动画的进度    private float t;    public Win8Search(Context context) {        super(context);    }    public Win8Search(Context context, AttributeSet attrs) {        super(context, attrs);        init();        valueAnimator.start();    }    private void init() {        mPaint = new Paint();        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(15);        mPaint.setColor(Color.WHITE);        //设置画笔为园笔        mPaint.setStrokeCap(Paint.Cap.ROUND);        //抗锯齿        mPaint.setAntiAlias(true);        mPath = new Path();        RectF rect = new RectF(-150,-150,150,150);        mPath.addArc(rect,-90,359.9f);        mPathMeasure = new PathMeasure(mPath,false);        valueAnimator = ValueAnimator.ofFloat(0f,1f).setDuration(3000);        valueAnimator.setRepeatCount(-1);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                t = (float) animation.getAnimatedValue();                invalidate();            }        });    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.translate(mWidth/2,mHeight/2);        Path dst = new Path();        if(t>=0.95){            canvas.drawPoint(0,-150,mPaint);        }        int num = (int) (t/0.05);        float s,y,x;        switch(num){            default:            case 3:                x = t-0.15f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 2:                x = t-0.10f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 1:                x = t-0.05f*(1-t);                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);            case 0:                x = t;                s = mPathMeasure.getLength();                y = -s*x*x+2*s*x;                mPathMeasure.getSegment(y,y+1,dst,true);                break;        }        canvas.drawPath(dst,mPaint);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mWidth = w;        mHeight = h;    }}
8 0
原创粉丝点击