仿华为01-圆环进度和小圆球

最终效果与原理

效果图

效果分为两方面
外围的进度显示,进度范围内的用红色表示,范围外灰色表示
滚动的小圆球,模拟类似重力加速,减速效果
实现原理
外围进度是由不同颜色的竖线表示的. 竖线以圆为路径分布.我们用 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
源码地址

猜你喜欢

转载自blog.csdn.net/hepann44/article/details/80736486
01-