最终效果与原理
-
效果分为两方面
- 外围的进度显示,进度范围内的用红色表示,范围外灰色表示
- 滚动的小圆球,模拟类似重力加速,减速效果 实现原理
-
外围进度是由不同颜色的竖线表示的. 竖线以圆为路径分布.我们用
canvas.rotate(float degrees, float px, float py)
方法来实现. 这样我们只需要画一条竖线->然后旋转画布->画同样的竖线->…即可. -
小圆球的位置同样用上面的方法实现. 我们只需要画同样的圆球即可. 圆球的动画我们用属性动画的 加速减速插值器
AccelerateDecelerateInterpolator()
实现.这样小球位置的变化就会先快后慢,似乎受到重力影响.
代码
public class ProgressCircle extends View {
Paint mPaint;
float mWidth;
float mHeight;
// 作图的最小范围
float minSize;
int mProgress = 10;
float mDotPosition = 0;
public ProgressCircle(Context context) {
this(context, null);
}
public ProgressCircle(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressCircle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.GRAY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = getWidth();
mHeight = getHeight();
minSize = Math.min(mWidth, mHeight) * 0.45f;
// 移动画布,方便计算
canvas.translate(mWidth / 2, mHeight / 2);
drawProgress(canvas);
drawCircle(canvas);
}
private void drawProgress(Canvas canvas) {
canvas.save();
for (int i = 1; i < 101; i++) {
if (mProgress >= i) {
mPaint.setColor(Color.RED);
} else {
mPaint.setColor(Color.GRAY);
}
// 刻度线的长度设置为 minSize 的 10%
canvas.drawLine(0, -minSize, 0, -(minSize * 0.9f), mPaint);
// 画布旋转
canvas.rotate(3.6f, 0, 0);
}
canvas.restore();
}
private void drawCircle(Canvas canvas) {
mPaint.setColor(Color.RED);
canvas.save();
canvas.rotate(mDotPosition * 3.6f, 0, 0);
canvas.drawCircle(0, -(minSize * 0.85f), minSize * 0.03f, mPaint);
canvas.restore();
}
// 开始小球动画
public void startCircleRun() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
animator.setDuration(1500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mDotPosition = (Float) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
public void setmProgress(int mProgress) {
if (mProgress >= 0 && mProgress <= 100) {
this.mProgress = mProgress;
invalidate();
}
}
}
-
备注
- AndroidStudio 版本 3.1.2 编译版本 compileSdkVersion 27
-
作图范围
minSize
是长宽较小值的近一半(0.45),其他参数如竖线高度,小球半径以作图宽度minSize
的占比来计算,以便适配 - 没有对外提供 attr 设置属性
-
对外暴露两个方法,开始动画:
startCircleRun
和设置进度setmProgress
- 源码地址