每周一个自定义View(1) -动态ProgressView
欢迎大家访问我的个人博客
新的一个系列, 应该是计划每周实现一个自定义View, 看看能坚持到多久吧
这次就从一个常见的ProgressBark开始吧, 最近的项目中使用了一个Progress显示文件下载进度的功能, 设计给的是一个静态的图片, 也没有说需要具体实现的情况, 后面优化的时候刚好有了性质, 就有了下面的这个AnimatorProgressBar.
效果展示
支持功能
- 基本的的进度设置(当前默认为0-100)
- 颜色定义, 使用的颜色都是可以设置的, 满足各样的ui需求
- 元素定义, 作为展示的Progress中的线条可以设置宽度和间距
- 动画控制, 动画效果可以设置展示速度, 总有一个组合适合你
设计过程
将相关的view分为了四层, 从下至上分别为
- 背景图层
用于显示整个view的背景 - 进度图层
相当于进度条的背景颜色 - 线段动画图层
在这里绘制出现的线段, 并控制器动画的效果 - 遮罩展示图层
这里使用了遮罩展示的方法, 控制遮罩图层的进度和样式来表现实际的展示效果
实现过程
首先是背景如果绘制的, 简单来说就是两个圆形和一个矩形, 具体的动画效果可以参考下面,
其它的图层基本上也都是比较类似的情况, 除此之外就是线条的实现
其中中心黑色的为展示的区域, 白色的是绘制的区域
代码实现
可编辑参数
//下面为可自定义编辑的参数
//背景颜色
private int viewBackGroundColor;
//线条颜色
private int viewLineColor;
//进度条颜色
private int viewProgressColor;
//线条见间隔
private int offsetLine;
//执行动画需要的时间
private int animatorTime;
//进度变化动画需要的时间
private int animatorProgressTime;
//画笔/线条的宽度
private int paintWidth;
背景图层&绘制图层背景
这两个图形的实现比较类似
/**
* @description 绘制图层背景
*/
private void getProgressBackground(Canvas canvas){
//设置画笔
Paint paint = new Paint();
paint.setColor(viewProgressColor);
//确定两个圆形的中心位置及半径
int leftCirclePoint = height / 2;
int rightCirclePoint = width - leftCirclePoint;
int radius = height / 2;
//绘制左侧和右侧的圆形填充
canvas.drawCircle(leftCirclePoint, height >> 1, radius, paint);
canvas.drawCircle(rightCirclePoint, height >> 1, radius, paint);
//绘制中心的矩形填充
Rect rect = new Rect(leftCirclePoint, 0, rightCirclePoint, height);
canvas.drawRect(rect, paint);
}
/**
* @description 绘制背景图层
*/
private void drawViewBackground(Canvas canvas) {
canvas.save();
Paint paint = new Paint();
paint.setColor(viewBackGroundColor);
int leftCirclePoint = height / 2;
int rightCirclePoint = width - leftCirclePoint;
int radius = height / 2;
canvas.drawCircle(leftCirclePoint, height >> 1, radius, paint);
canvas.drawCircle(rightCirclePoint, height >> 1, radius, paint);
Rect rect = new Rect(leftCirclePoint, 0, rightCirclePoint, height);
canvas.drawRect(rect, paint);
}
绘制遮罩图层
/**
* @description 绘制遮罩图层
*/
private Bitmap progressBitmapDst(){
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//如果当前进度为0, 则不展示任何内容
if (progress == 0){
return bitmap;
}
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
//遮罩图层宽度与当前progress进度有关
int leftCirclePoint = height / 2;
int rightCirclePoint = leftCirclePoint + (int) ((width - height) / 1f * progress / 100f);
int radius = height / 2;
canvas.drawCircle(leftCirclePoint, height >> 1, radius, paint);
canvas.drawCircle(rightCirclePoint, height >> 1, radius, paint);
Rect rect = new Rect(leftCirclePoint, 0, rightCirclePoint, height);
canvas.drawRect(rect, paint);
return bitmap;
}
绘制线条动画
/**
* @description 绘制线条动画
*/
private void getProgressLines(Canvas canvas){
Paint paint = new Paint();
paint.setColor(viewLineColor);
//设置画笔宽度
paint.setStrokeWidth(paintWidth);
//设置画笔圆形边界
paint.setStrokeCap(Paint.Cap.ROUND);
//todo 动画效果展示优化
canvas.translate(-offsetLine * 2, 0);
//offsetAnimator 为动画展示时使用, 每次移动一定的长度,
//通过连续的移动来实现视觉上的动画效果
canvas.translate(offsetAnimator, 0);
canvas.save();
int indexOffset = 0;
//依次绘制线条
do {
//绘制右上到左下的线条
canvas.drawLine(20, -20, -offsetLine - 20, height + 20, paint);
indexOffset += offsetLine;
//偏移位置
canvas.translate(offsetLine, 0);
}while (indexOffset < width + offsetLine * 4);
canvas.restore();
}
动画效果
/**
* @description 进度变化动画
* @param startProgress
* @param endProgress
*/
public void startProgressAnimator(int startProgress, int endProgress){
//动画差值范围
ValueAnimator va = ValueAnimator.ofFloat(startProgress, endProgress);
va.setDuration(animatorProgressTime);
//线性取值
va.setInterpolator(new LinearInterpolator());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
progress = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
va.start();
}
/**
* @description 开始动画
*/
public void startAnimator(){
//动画差值范围
ValueAnimator va = ValueAnimator.ofFloat(0f, offsetLine);
//循环播放
va.setRepeatCount(ValueAnimator.INFINITE);
va.setDuration(animatorTime);
//线性取值
va.setInterpolator(new LinearInterpolator());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
offsetAnimator = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
va.start();
}
最后
相关代码可以访问我的GitHub来获取,欢迎大家start或者提供建议.