Android自定义圆形进度表

很早之前项目中需要展示进度管理做的,今天总结一下。为后面更复杂的自定义view打下基础。

功能很简单,但是里面涉及了两个自定义控件很重要的两个方法 onMeasure 和 onDraw,用来确定控件的大小和内容

话不多说先看效果

自定义view 大体分为三类:

  1. 直接继承View

  1. 继承ViewGroup

  1. 继承原有控件

根据项目需求选择最优的实现方式。

因为当前的view是一个圆形进度图所以它的宽高是固定并且相等的,重写onMeasure设置当前view的大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(Math.min(width,height),Math.min(width,height));
    }

当前控件选择继承view来实现。可以看到当前view可以分三个部分 外圈弧、内圈弧、进度文字。根据这三个部分一步步去实现。

首先绘制外弧背景 角度为0到360度

   //画背景弧
   canvas.drawArc(left, top,right, bottom, 0,
      360, false, backProgressPaint);

画内弧 -90为圆弧绘制的开始角度,因为内弧的角度和进度有关,所以我们可以定义一个变量sweepAngle 在变化时去进行重绘

 //进度弧
   canvas.drawArc(left, top,right, bottom, -90,
      sweepAngle, false, progressPaint);

画进度文字,使用canvas绘制文字的时候需要去计算当前文字的基线才能使文字居中

  Rect rect = new Rect();
  textPaint.getTextBounds(mProgress,0,mProgress.length(),rect);
  float dx = getWidth()/2-rect.width()/2;
// 获取基线
  Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
  int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
  int baseLine = getHeight() / 2 + dy;

   canvas.drawText(mProgress,dx, baseLine,textPaint);

绘制已经完成了,想要动起来需要添加数值动画,监听回调并调用invalidate方法进行重绘操作

 private void startAnimator(){
        //当前数值变化从0到1
        valueAnimator = ValueAnimator.ofFloat(1);
        valueAnimator.setDuration(4000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animationValue = (float) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: "+animationValue);
                sweepAngle = (int) (360 * animationValue);
                mProgress =  (int) (animationValue * 100) + "%";
                //进行重绘
                invalidate();
            }
        });
        valueAnimator.start();
    }

完整代码如下:

public class CircularProgress extends View{

    private static final String TAG = "CircularProgress";

    // 文字画笔
    Paint textPaint;
    // 弧形背景画笔
    Paint backProgressPaint;
    // 弧形进度画笔
    Paint progressPaint;
    //进度
    String mProgress = "0%";

    int textColor = R.color.color_009ad6;

    int backProgressColor = R.color.color_90d7ec;

    int progressColor = R.color.color_009ad6;

    //圆弧的宽度
    float progressWidgt = UtilView.dip2px(20);

    Context mContext;

    int sweepAngle = 0;

    ValueAnimator valueAnimator = null;

    public CircularProgress(Context context) {
        this(context,null);
    }

    public CircularProgress(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircularProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        textPaint = initPaint(textColor);
        textPaint.setTextSize(UtilView.sp2px(18));
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);

        backProgressPaint = initPaint(backProgressColor);
        backProgressPaint.setStrokeWidth(progressWidgt);
        backProgressPaint.setStyle(Paint.Style.STROKE);
        backProgressPaint.setColor(Color.parseColor("#90d7ec"));

        progressPaint = initPaint(progressColor);
        progressPaint.setStrokeWidth(progressWidgt);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setColor(Color.parseColor("#009ad6"));
        progressPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    private Paint initPaint(int color) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(color);
        return paint;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(Math.min(width,height),Math.min(width,height));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        float left = 0 + progressWidgt / 2;
        float top = 0 + progressWidgt / 2;
        float right = getWidth() - progressWidgt / 2;
        float bottom = getHeight() - progressWidgt / 2;
        //画背景弧
        canvas.drawArc(left, top,right, bottom, 0,
        360, false, backProgressPaint);

        //进度弧
        canvas.drawArc(left, top,right, bottom, -90,
                sweepAngle, false, progressPaint);

        Rect rect = new Rect();
        textPaint.getTextBounds(mProgress,0,mProgress.length(),rect);
        float dx = getWidth()/2-rect.width()/2;
        // 获取基线
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
        int baseLine = getHeight() / 2 + dy;

        canvas.drawText(mProgress,dx, baseLine,textPaint);


    }


    public void setStateProgress(){
        startAnimator();
    }

    private void startAnimator(){
        valueAnimator = ValueAnimator.ofFloat(1);
        valueAnimator.setDuration(4000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animationValue = (float) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: "+animationValue);
                sweepAngle = (int) (360 * animationValue);
                mProgress =  (int) (animationValue * 100) + "%";
                invalidate();
            }
        });
        valueAnimator.start();
    }

    public void stopAnimator(){
        if(valueAnimator != null){
            valueAnimator.cancel();
            valueAnimator = null;
        }
    }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
总共分为三层:一层为圆形边线,一层为进度边线,一层用来显示标识进度节点。 public class CircleProgressBar extends View { private int maxProgress = 100; private int progress = 15; private int progressStrokeWidth = 2; private int marxArcStorkeWidth = 16; // 画圆所在的距形区域 RectF oval; Paint paint; public CircleProgressBar(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub oval = new RectF(); paint = new Paint(); } @Override protected void onDraw(Canvas canvas) { // TODO 自动生成的方法存根 super.onDraw(canvas); int width = this.getWidth(); int height = this.getHeight(); width = (width > height) ? height : width; height = (width > height) ? height : width; paint.setAntiAlias(true); // 设置画笔为抗锯齿 paint.setColor(Color.WHITE); // 设置画笔颜色 canvas.drawColor(Color.TRANSPARENT); // 白色背景 paint.setStrokeWidth(progressStrokeWidth); // 线宽 paint.setStyle(Style.STROKE); oval.left = marxArcStorkeWidth / 2; // 左上角x oval.top = marxArcStorkeWidth / 2; // 左上角y oval.right = width - marxArcStorkeWidth / 2; // 左下角x oval.bottom = height - marxArcStorkeWidth / 2; // 右下角y canvas.drawArc(oval, -90, 360, false, paint); // 绘制白色圆圈,即进度条背景 paint.setColor(Color.rgb(0x57, 0x87, 0xb6)); paint.setStrokeWidth(marxArcStorkeWidth); canvas.drawArc(oval, -90, ((float) progress / maxProgress) * 360, false, paint); // 绘制进度圆弧,这里是蓝色 paint.setStrokeWidth(1); String text = progress + "%"; int textHeight = height / 4; paint.setTextSize(textHeight); int textWidth = (int) paint.measureText(text, 0, text.length()); paint.setStyle(Style.FILL); canvas.drawText(text, width / 2 - textWidth / 2, height / 2 + textHeight / 2, paint); } public int getMaxProgress() { return maxProgress; } public void setMaxProgress(int maxProgress) { this.maxProgress = maxProgress; } /** * 设置进度 * * @param progress * 进度百分比 * @param view * 标识进度的节点视图 */ public void setProgress(int progress, View view) { this.progress = progress; view.setAnimation(pointRotationAnima(0, (int) (((float) 360 / maxProgress) * progress))); this.invalidate(); } /** * 非UI线程调用 */ public void setProgressNotInUiThread(int progress, View view) { this.progress = progress; view.setAnimation(pointRotationAnima(0, (int) (((float) 360 / maxProgress) * progress))); this.postInvalidate(); } /** * 进度标注点的动画 * * @param fromDegrees * @param toDegrees * @return */ private Animation pointRotationAnima(float fromDegrees, float toDegrees) { int initDegress = 306;// 进度点起始位置(图片偏移约54度) RotateAnimation animation = new RotateAnimation(fromDegrees, initDegress + toDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(1);// 设置动画执行时间 animation.setRepeatCount(1);// 设置重复执行次数 animation.setFillAfter(true);// 设置动画结束后是否停留在结束位置 return animation; } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值