Android 自定义 View 之 圆形进度条

先上一张效果图,见下图:
圆形进度条

基础

等待更新

CycleProgress

1. 创建 CycleProgress

创建一个CycleView类,使其继承子View, 重写它的三个构造方法

public class CycleProgress extends View {
    public CycleProgress(Context context) {
        super(context);
        init(context, null);
    }

    public CycleProgress(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public CycleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs); // 初始化 view
    }
}

在构造方法的init中,我们完成View的初始化,

2.创建布局

新建一个activity_cycleprogress,简单点如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cyclepb="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.hong.widgetstyle.ui.widget.CycleProgress
        android:layout_centerInParent="true"
        android:id="@+id/cycle_progress"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:layout_marginTop="20dp"
        cyclepb:centerProgressTextColor="@color/colorAccent"
        cyclepb:circleBgStrokeWidth="10dp"
        cyclepb:progressStrokeWidth="10dp"
        cyclepb:centerProgressTextSize="14sp"
        cyclepb:isDrawCenterProgressText="true"/>
</RelativeLayout>

其中:xmlns:cyclepb=”http://schemas.android.com/apk/res-auto” 被称作命名空间,
它的理解和xmlns:android=”http://schemas.android.com/apk/res/android” 是一样的,之所以我们能用下面的layout_width等属性,是因为android 内部定义了一个名为 android 的各种属性, 所以我们也可以仿照它自定义一系列属性,叫做cyclepb。

3. 创建自定义属性

在res/values文件夹下,创建atter.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CycleProgress">
        <attr name="circleBgStrokeWidth" format="dimension" />  <!--圈的宽度 -->
        <attr name="progressStrokeWidth" format="dimension" />  <!--进度条的宽度 -->

        <attr name="circleBgColor" format="color" />    <!--圈的颜色 -->
        <attr name="progressColor" format="color" />    <!--进度条的颜色 -->

        <attr name="circleAnimationDuration" format="integer" />  <!--进度条时长 -->

        <attr name="isDrawCenterProgressText" format="boolean" /> <!--是否显示圈内文字 -->

        <attr name="centerProgressTextColor" format="color"/>  <!--圈内文字的颜色 -->
        <attr name="centerProgressTextSize" format="dimension"/> <!--圈内文字的大小 -->
    </declare-styleable>
</resources>

name为 你的自定义控件的类名
其中的这些attr 可以在布局文件中进行引用,设置具体数值

4. 初始化CycleProgress

前面我们说的在init中 进行view的初始化 ,

private void init(Context context, AttributeSet attrs) {
        this.mContext = context;
        getAttr(attrs);  // 获取控件属性,
        initPaint(); // 初始化圆圈画笔
        initTextPaint(); // 初始化文字画笔
    }

首先我们需要获取到我们设置的属性,动态的进行设置值, 然后就是初始化画笔, 也就是Paint, 最后在onDraw中canvas上画出来就可以了。简单点就是用画笔(Paint) 在画布(Canvas)上把我们需要的形状画出来。

4.1 获取控件属性
   /**
     * 获取控件属性(命名空间)
     */
    private void getAttr(AttributeSet attrs) {
        TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CycleProgress);
        cycleBgStrokeWidth = typedArray.getDimensionPixelOffset(R.styleable.CycleProgress_circleBgStrokeWidth, 10);
        progressStrokeWidth = typedArray.getDimensionPixelOffset(R.styleable.CycleProgress_progressStrokeWidth, 10);
        isProgressText = typedArray.getBoolean(R.styleable.CycleProgress_isDrawCenterProgressText, false);
        centerTextColor = typedArray.getColor(R.styleable.CycleProgress_centerProgressTextColor, mContext.getResources().getColor(R.color.colorAccent));
        centerTextSize = typedArray.getDimensionPixelOffset(R.styleable.CycleProgress_centerProgressTextSize, 16);
        cycleBgColor = typedArray.getColor(R.styleable.CycleProgress_circleBgColor, mContext.getResources().getColor(R.color.colorPrimary));
        progressColor = typedArray.getColor(R.styleable.CycleProgress_progressColor, mContext.getResources().getColor(R.color.colorAccent));

        typedArray.recycle();
    }
    private int cycleBgStrokeWidth;
    private int progressStrokeWidth;
    private boolean isProgressText;
    private int centerTextColor;
    private int centerTextSize;
    private int cycleBgColor;
    private int progressColor;
4.2 初始化圆圈的画笔(圈分为没进度条的圈和进度条的圈)
    /**
     * 初始化圆圈画笔
     */
    private void initPaint() {
        progressPaint = getPaint(progressStrokeWidth, progressColor); // 进度条画笔
        cycleBgPaint = getPaint(cycleBgStrokeWidth, cycleBgColor);  // 无进度条圈的画笔
    }

    private Paint getPaint(int width, int color) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(width);
        // Paint.Style.FILL:填充内部  Paint.Style.FILL_AND_STROKE:填充内部和描边Paint.Style.STROKE :描边
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(color);
        paint.setAntiAlias(true); // 扛锯齿
        paint.setStrokeCap(Paint.Cap.ROUND); // 两端是圆角
        return paint;
    }
4.3 初始化圈内文字的画笔
    /**
     * 初始化文字画笔
     */

    private void initTextPaint() {
        centerTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerTextPaint.setColor(centerTextColor);
        centerTextPaint.setTextAlign(Paint.Align.CENTER);
        centerTextPaint.setTextSize(centerTextSize);
        centerTextPaint.setAntiAlias(true);
    }
4.4 重写onSizeChanged() 方法
 /**
     * view发生改变的时候调用
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        centerX = w / 2;
        centerY = h / 2;
        radius = Math.min(w, h) / 2 - Math.max(cycleBgStrokeWidth, progressStrokeWidth); // 两数中的最小值 / 2 - 两数中的最大值

        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
    }
/**
     * 圆心x坐标
     */
    private float centerX;
    /**
     * 圆心y坐标
     */
    private float centerY;
    /**
     * 圆的半径
     */
    private float radius;
    /**
     * 进度
     */
    private float mProgress;

    /**
     * 当前进度
     */
    private float currentProgress;
4.5 重写onDraw() 进行绘制
 /**
     * 画
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(centerX, centerY, radius, cycleBgPaint);
        canvas.drawArc(rectF, 90, currentProgress, false, progressPaint);
        if (isProgressText) {
            Paint.FontMetrics fontMetrics = centerTextPaint.getFontMetrics();
            int baseline = (int) ((rectF.bottom + rectF.top - fontMetrics.bottom - fontMetrics.top) / 2);
            canvas.drawText((int) mProgress + "%", rectF.centerX(), baseline, centerTextPaint);
        }
    }
4.6 添加动画

我们已将将这个进度条的圈画出来了,为了让它动起来, 这里我们添加属性动画ValueAnimator

 /**
     * 初始化动画
     */

    private void initAanimator() {
        valueAnimator = ValueAnimator.ofFloat(0, mProgress);
        valueAnimator.setDuration(duration);
        valueAnimator.setStartDelay(500);
        //  ——AccelerateInterpolator:动画从开始到结束,变化率是一个加速的过程。
        //——DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。
        //——CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线。
        // ——AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。
        //——LinearInterpolator:动画从开始到结束,变化率是线性变化。
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { //
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float value = (float)valueAnimator.getAnimatedValue();
                mProgress = value;
                currentProgress = value * 360 / 100;
                invalidate(); // 别忘记刷新
            }
        });
    }
4.7 在对应Activity中设置动画
/**
     * 设置动画
     * @param progress
     */
    public void setAnimator(float progress) {
        this.mProgress = progress;
        initAanimator();
    }

    /**
     * 开始动画
     */
    public void startAnimator() {
        valueAnimator.start();
    }

    public void stopAnimator() { // 在onDestory中停止
        valueAnimator.end();
    }
 cycleProgress = (CycleProgress) findViewById(R.id.cycle_progress);
 cycleProgress.setAnimator(100);
 cycleProgress.startAnimator();

工程Demo地址:https://github.com/Trac-MacGrady/WidgetStyleView

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值