android自定义view 模仿win10进度条
来源:互联网 发布:微信运动的数据来源 编辑:程序博客网 时间:2024/05/16 01:55
android自定义view 模仿win10进度条
本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发。
PS:有朋友反映动画无法播放,那是因为PathMeasure.getLength()只在KITKAT以上版本有效,而对于以下版本一定要关闭硬件加速才有效。
先上预览图:
流程
- 1.一个匀速圆周运动的点
- 2.多个匀速圆周运动的点
- 3.多个圆周运动的点,速度由快到慢
- 4.点与点之间的间距线性减少,动画的最后合为一个点
- 5.为了让动画看起来更加流畅,需要在动画即将结束的时候手动绘制点
核心控件
- PathMeasure:截取Path中的一部分并显示
- ValueAnimator:完成动画从初始值平滑的过度到结束值的效果,同时还负责管理动画的播放次数、播放模式、以及对动画设置监听器等
流程一
思路
- 先用path画一个圆
- ValueAnimator设置为0f-1f的平滑
- 用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; }}
- android自定义view 模仿win10进度条
- android自定义view 模仿win10进度条
- android自定义view 模仿win10进度条
- android自定义view 模仿win10进度条
- Android自定义View绘制圆形、方形、弧形、球形四种形态的模仿下载进度条
- android 圆形进度条 自定义view
- android 自定义view 圆形进度条
- Android自定义View---圆形进度条
- Android 仪表进度条 自定义View
- Android自定义view 圆环进度条
- Android-------自定义View圆形进度条
- Android自定义view(圆形进度条)
- Android自定义view圆形进度条
- Android-自定义View-竞赛进度条
- Android自定义view圆形进度条
- Android自定义View-圆形进度条
- Android自定义View画圆+进度条+自定义View梯形
- Android自定义View之实现环形进度条
- Codeforces Round #371 (Div. 2) B. Filya and Homework(水题)
- Leetcode 106. Construct Binary Tree from Inorder and Postorder Traversal 中序和后序恢复二叉树
- 20160916IOS音频开发之资源与元数据,解决获取资源属性问题(一)
- python处理数据——去除字符串两端的引号
- 商品添加和减少
- android自定义view 模仿win10进度条
- 计算机的组成及其功能
- Linux笔记--shell总结(2)
- Python函数式编程指南(三):迭代器
- C#(asp.net)哈希表
- 设计模式之状态模式
- File对象的创建
- SpringMVC request生命周期
- dos下的cd操作