android 自定义 View(4)- 进度条(ProgressBar)

来源:互联网 发布:2017知乎用户调查报告 编辑:程序博客网 时间:2024/05/01 02:16

参考:

Android 打造形形色色的进度条 实现可以如此简单
daimajia/NumberProgressBar


自定义视图(View)是 Android 开发的一个进阶内容。随着开发的深入,肯定会出现系统提供的基础控件不符合需求的情况。一方面通过组合基础控件以形成新的布局,另一方面可以通过自定义控件的方式来更加灵活的实现需求

自定义视图涉及到 Android 系统许多方面的内容,下面根据自己的理解顺序来讲一讲如何自定义视图


主要内容

  1. 进度条浅析
  2. 水平进度条
  3. 文本进度条
  4. 圆形进度条

参考:ProgressBar

进度条是 Android 应用中经常使用的一种控件,在文件上传下载时,用进度条显示当前上传或者下载进度,更有利于用户体验

最简单的进度条应该就是一条线段,用两种颜色表示已完成和未完成,如果在线段上加上文本显示,更有利于说明当前进度,另外,还可以制作圆形进度条


水平进度条

学习 hongyang 的自定义进度条,利用 Android 内置的 ProgressBar,重新绘制进度条。

下面实现最简单的水平进度条

水平进度条浅析

实现水平进度条,首先是确定进度条的颜色,可以先默认设置已完成和未完成进度条的颜色,然后利用控件 ProgressBar 的属性设置当前进度以及总进度值

通过自定义属性,可以设置颜色值

根据控件长宽属性,进行尺寸的重新测量,确定进度条的大小,实现更完善的水平进度条

实现进度条

新建 SimpleProgressbar.java,继承 Android 控件 ProgressBar设置进度条的颜色,在 onDraw 方法中利用 ProgressBar 提供的属性 android:progress 设置当前进度

public class SimpleProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    public static final int DEFAULT_UNREACHED_COLOR = 0xFF912CEE;    public static final int DEFAULT_REACHED_COLOR = 0xFF54FF9F;    /**     * 画笔     */    private Paint paint;    /**     * 未到达进度条颜色     */    private int unreachedColor;    /**     * 已到达进度条颜色     */    private int reachedColor;    public SimpleProgressbar(Context context) {        //        super(context);        this(context, null);    }    public SimpleProgressbar(Context context, AttributeSet attrs) {        //        super(context, attrs);        this(context, attrs, 0);    }    public SimpleProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;    }    @Override    protected synchronized void onDraw(Canvas canvas) {        //        super.onDraw(canvas);        // 获取画布的宽高        int width = getWidth();        int height = getHeight();        // 获取进度条的实际宽高        int lineWidth = width - getPaddingLeft() - getPaddingRight();        int lineHeight = height - getPaddingTop() - getPaddingBottom();        // 获取当前进度        float ratio = getProgress() * 1.0f / getMax();        // 获取未完成进度大小        int unreachedWidth = (int) (lineWidth * (1 - ratio));        // 获取已完成进度大小        int reachedWidth = lineWidth - unreachedWidth;        // 绘制已完成进度条,设置画笔颜色和大小        paint.setColor(reachedColor);        paint.setStrokeWidth(lineHeight);        // 计算已完成进度条起点和终点的坐标        int startX = getPaddingLeft();        int startY = getHeight() / 2;        int stopX = startX + reachedWidth;        int stopY = startY;        // 画线        canvas.drawLine(startX, startY, stopX, stopY, paint);        // 设置画笔颜色        paint.setColor(unreachedColor);        startX = getPaddingLeft() + reachedWidth;        stopX = width - getPaddingRight();        canvas.drawLine(startX, startY, stopX, stopY, paint);    }}

布局文件如下:

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.zj.progressnumber.MainActivity">    <com.zj.progressnumber.SimpleProgressbar        android:layout_width="match_parent"        android:layout_height="30dp"        android:layout_marginTop="30dp"        android:background="@android:color/holo_orange_dark"        android:padding="8dp"        android:progress="30" /></android.support.design.widget.CoordinatorLayout>

这里写图片描述

每隔 500 毫秒增加进度条进度,实现如下:

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final SimpleProgressbar spb = (SimpleProgressbar) findViewById(R.id.spb);        final int max = spb.getMax();        new Thread(new Runnable() {            @Override            public void run() {                int progress = spb.getProgress();                while ((progress + 1) <= max) {                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    spb.setProgress(progress + 1);                    progress = progress + 1;                }            }        }).start();    }}

这里写图片描述

自定义进度条颜色

自定义已完成进度条和未完成进度条的颜色

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="reachedColor" format="color" />    <attr name="unreachedColor" format="color" />    <declare-styleable name="SimpleProgressbar">        <attr name="reachedColor" />        <attr name="unreachedColor" />    </declare-styleable></resources>

修改 SimpleProgressbar.java,增加自定义属性的查询:

public class SimpleProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    ...    ...    public SimpleProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        ...        obtainStyledAttributes(context, attrs, defStyleAttr);    }    ...    ...    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SimpleProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.SimpleProgressbar_reachedColor:                    reachedColor = a.getColor(attr, DEFAULT_REACHED_COLOR);                    break;                case R.styleable.SimpleProgressbar_unreachedColor:                    unreachedColor = a.getColor(attr, DEFAULT_UNREACHED_COLOR);                    break;            }        }        a.recycle();    }}

修改布局文件:

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:custom="http://schemas.android.com/apk/res/com.zj.progressnumber"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.zj.progressnumber.MainActivity">    <com.zj.progressnumber.SimpleProgressbar        android:id="@+id/spb"        android:layout_width="match_parent"        android:layout_height="30dp"        android:layout_marginTop="30dp"        android:background="@android:color/holo_orange_dark"        android:padding="8dp"        android:progress="30"        custom:reachedColor="@android:color/holo_green_light"        custom:unreachedColor="@android:color/holo_blue_light"        /></android.support.design.widget.CoordinatorLayout>

这里写图片描述

修改程序,随机增加进度:

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final SimpleProgressbar spb = (SimpleProgressbar) findViewById(R.id.spb);        final int max = spb.getMax();        new Thread(new Runnable() {            @Override            public void run() {                int progress = spb.getProgress();                int random = (int) (Math.random() * 10) + 1;                random = (progress + random) <= max ? random : (max - progress);                while ((progress + random) <= max) {                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    progress = progress + random;                    spb.setProgress(progress);                    if (progress == max)                        break;                    random = (int) (Math.random() * 10) + 1;                    random = (progress + random) <= max ? random : (max - progress);                }            }        }).start();    }}

这里写图片描述

设定进度条宽高

在上面的代码中,如果进度条宽高设定为 wrap_content 模式,无法正常显示,修改代码,设定最小的宽和高

完整代码如下:

public class SimpleProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    public static final int DEFAULT_UNREACHED_COLOR = 0xFF912CEE;    public static final int DEFAULT_REACHED_COLOR = 0xFF54FF9F;    // 进度条默认高,单位为 dp    public static final int DEFAULT_LINE_HEIGHT = 2;    // 进度条默认宽,单位为 dp    public static final int DEFAULT_LINE_WIDTH = 100;    /**     * 画笔     */    private Paint paint;    /**     * 未到达进度条颜色     */    private int unreachedColor;    /**     * 已到达进度条颜色     */    private int reachedColor;    /**     * 默认进度条最小的高(不含内边距)     */    private int minLineHeight;    /**     * 默认进度条最小的宽(不含内边距)     */    private int minLineWidth;    /**     * 实际使用的进度条的高(不含内边距)     */    private int lineHeight;    /**     * 实际使用的进度条的宽(不含内边距)     */    private int lineWidth;    public SimpleProgressbar(Context context) {        //        super(context);        this(context, null);    }    public SimpleProgressbar(Context context, AttributeSet attrs) {        //        super(context, attrs);        this(context, attrs, 0);    }    public SimpleProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;        minLineHeight = dp2px(DEFAULT_LINE_HEIGHT);        minLineWidth = dp2px(DEFAULT_LINE_WIDTH);        obtainStyledAttributes(context, attrs, defStyleAttr);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int desiredWidth = minLineWidth + getPaddingLeft() + getPaddingRight();        int desiredHeight = minLineHeight + getPaddingTop() + getPaddingBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = Math.min(widthSize, desiredWidth);        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = Math.min(heightSize, desiredHeight);        } else {            height = Math.max(heightSize, desiredHeight);        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {        //        super.onDraw(canvas);        // 获取画布的宽高        int width = getWidth();        int height = getHeight();        // 获取进度条的实际宽高        int lineWidth = width - getPaddingLeft() - getPaddingRight();        int lineHeight = height - getPaddingTop() - getPaddingBottom();        // 获取当前进度        float ratio = getProgress() * 1.0f / getMax();        // 获取未完成进度大小        int unreachedWidth = (int) (lineWidth * (1 - ratio));        // 获取已完成进度大小        int reachedWidth = lineWidth - unreachedWidth;        // 绘制已完成进度条,设置画笔颜色和大小        paint.setColor(reachedColor);        paint.setStrokeWidth(lineHeight);        // 计算已完成进度条起点和终点的坐标        int startX = getPaddingLeft();        int startY = getHeight() / 2;        int stopX = startX + reachedWidth;        int stopY = startY;        // 画线        canvas.drawLine(startX, startY, stopX, stopY, paint);        // 设置画笔颜色        paint.setColor(unreachedColor);        startX = getPaddingLeft() + reachedWidth;        stopX = width - getPaddingRight();        canvas.drawLine(startX, startY, stopX, stopY, paint);    }    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SimpleProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.SimpleProgressbar_reachedColor:                    reachedColor = a.getColor(attr, DEFAULT_REACHED_COLOR);                    break;                case R.styleable.SimpleProgressbar_unreachedColor:                    unreachedColor = a.getColor(attr, DEFAULT_UNREACHED_COLOR);                    break;            }        }        a.recycle();    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }}

文本进度条

上面已实现了水平进度条,如果要在进度条上增加文本,需要考虑以下几点:

  • 如何平衡文本和进度条的大小
  • 如果插入文本

思路:判断文本的高和进度条的高,设置更大的值为文本进度条的高;文本值从 0% 变化到 100%,在水平进度条上固定预留出文本显示最大值的宽

首先还是先把文本进度条画出来,实现如下,新建 ZProgressbar.java

public class ZProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    public static final int DEFAULT_UNREACHED_COLOR = 0xFF7D9EC0;    public static final int DEFAULT_REACHED_COLOR = 0xFFC1FFC1;    public static final int DEFAULT_TEXT_COLOR = 0xFF0000CD;    public static final String DEFAULT_TEXT = "100%";    // 进度条默认高,单位为 dp    public static final int DEFAULT_LINE_HEIGHT = 2;    // 进度条默认宽,单位为 dp    public static final int DEFAULT_LINE_WIDTH = 100;    // 文本大小,单位为 sp    public static final int DEFAULT_TEXT_SIZE = 12;    private Paint paint;    private Rect textBound;    private int reachedColor;    private int unreachedColor;    private int textColor;    private int lineHeight;    private int minLineHeight;    private int minLineWidth;    private int textSize;    private int textHeight;    private int textWidth;    public ZProgressbar(Context context) {        //        super(context);        this(context, null);    }    public ZProgressbar(Context context, AttributeSet attrs) {        //        super(context, attrs);        this(context, attrs, 0);    }    public ZProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        textBound = new Rect();        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;        textColor = DEFAULT_TEXT_COLOR;        minLineHeight = dp2px(DEFAULT_LINE_HEIGHT);        minLineWidth = dp2px(DEFAULT_LINE_WIDTH);        textSize = sp2px(DEFAULT_TEXT_SIZE);        // 计算文本的宽和高        paint.setTextSize(textSize);        paint.getTextBounds(DEFAULT_TEXT, 0, DEFAULT_TEXT.length(), textBound);        textWidth = textBound.width();        textHeight = textBound.height();    }    @Override    protected synchronized void onDraw(Canvas canvas) {        //        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        lineHeight = height - getPaddingTop() - getPaddingBottom();        float ratio = getProgress() * 1.0f / getMax();        int unreachedWidth = (int) ((contentWidth - textWidth) * (1 - ratio));        int reachedWidth = contentWidth - textWidth - unreachedWidth;        paint.setColor(reachedColor);        paint.setStrokeWidth(lineHeight);        int startX = getPaddingLeft();        int startY = height / 2;        int stopX = getPaddingLeft() + reachedWidth;        int stopY = height / 2;        canvas.drawLine(startX, startY, stopX, stopY, paint);        String currentText = getProgress() + "%";        paint.getTextBounds(currentText, 0, currentText.length(), textBound);        paint.setColor(textColor);        paint.setTextSize(textSize);        startX = getPaddingLeft() + reachedWidth + (textWidth - textBound.width()) / 2;        Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();        startY = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;        canvas.drawText(currentText, startX, startY, paint);        paint.setColor(unreachedColor);        paint.setStrokeWidth(lineHeight);        startX = getPaddingLeft() + reachedWidth + textWidth;        startY = height / 2;        stopX = width - getPaddingRight();        stopY = height / 2;        canvas.drawLine(startX, startY, stopX, stopY, paint);    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }    /**     * sp 2 px     *     * @param spVal     * @return     */    protected int sp2px(int spVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                spVal, getResources().getDisplayMetrics());    }}

布局文件如下:

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:custom="http://schemas.android.com/apk/res/com.zj.progressnumber"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:padding="8dp"    tools:context="com.zj.progressnumber.MainActivity">    <com.zj.progressnumber.ZProgressbar        android:id="@+id/zpb"        android:layout_width="match_parent"        android:layout_height="30dp"        android:layout_marginTop="100dp"        android:progress="10" /></android.support.design.widget.CoordinatorLayout>

这里写图片描述

程序实现如下:

这里写图片描述

接下里增加尺寸测量部分:

public class ZProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    public static final int DEFAULT_UNREACHED_COLOR = 0xFF7D9EC0;    public static final int DEFAULT_REACHED_COLOR = 0xFFC1FFC1;    public static final int DEFAULT_TEXT_COLOR = 0xFF0000CD;    public static final String DEFAULT_TEXT = "100%";    // 进度条默认高,单位为 dp    public static final int DEFAULT_LINE_HEIGHT = 2;    // 进度条默认宽,单位为 dp    public static final int DEFAULT_LINE_WIDTH = 100;    // 文本大小,单位为 sp    public static final int DEFAULT_TEXT_SIZE = 12;    private Paint paint;    private Rect textBound;    private int reachedColor;    private int unreachedColor;    private int textColor;    private int lineHeight;    private int minLineHeight;    private int minLineWidth;    private int textSize;    private int textHeight;    private int textWidth;    public ZProgressbar(Context context) {        //        super(context);        this(context, null);    }    public ZProgressbar(Context context, AttributeSet attrs) {        //        super(context, attrs);        this(context, attrs, 0);    }    public ZProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        textBound = new Rect();        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;        textColor = DEFAULT_TEXT_COLOR;        minLineHeight = dp2px(DEFAULT_LINE_HEIGHT);        minLineWidth = dp2px(DEFAULT_LINE_WIDTH);        textSize = sp2px(DEFAULT_TEXT_SIZE);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        // 计算文本的宽和高        paint.setTextSize(textSize);        paint.getTextBounds(DEFAULT_TEXT, 0, DEFAULT_TEXT.length(), textBound);        textWidth = textBound.width();        textHeight = textBound.height();        // 比较文本的高和线段的高        int minHeight = textHeight > minLineHeight ? textHeight : minLineHeight;        int desiredWidth = minLineWidth + getPaddingLeft() + getPaddingRight();        int desiredHeight = minHeight + getPaddingTop() + getPaddingBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = desiredWidth;        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = desiredHeight;            lineHeight = minLineHeight;        } else {            height = Math.max(heightSize, desiredHeight);            lineHeight = height - getPaddingLeft() - getPaddingRight();        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {        //        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        float ratio = getProgress() * 1.0f / getMax();        int unreachedWidth = (int) ((contentWidth - textWidth) * (1 - ratio));        int reachedWidth = contentWidth - textWidth - unreachedWidth;        paint.setColor(reachedColor);        paint.setStrokeWidth(lineHeight);        int startX = getPaddingLeft();        int startY = height / 2;        int stopX = getPaddingLeft() + reachedWidth;        int stopY = height / 2;        canvas.drawLine(startX, startY, stopX, stopY, paint);        String currentText = getProgress() + "%";        paint.getTextBounds(currentText, 0, currentText.length(), textBound);        paint.setColor(textColor);        paint.setTextSize(textSize);        startX = getPaddingLeft() + reachedWidth + (textWidth - textBound.width()) / 2;        Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();        startY = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;        canvas.drawText(currentText, startX, startY, paint);        paint.setColor(unreachedColor);        paint.setStrokeWidth(lineHeight);        startX = getPaddingLeft() + reachedWidth + textWidth;        startY = height / 2;        stopX = width - getPaddingRight();        stopY = height / 2;        canvas.drawLine(startX, startY, stopX, stopY, paint);    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }    /**     * sp 2 px     *     * @param spVal     * @return     */    protected int sp2px(int spVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                spVal, getResources().getDisplayMetrics());    }}

最后增加自定义属性,控制文本颜色,进度条颜色,文本大小以及是否显示文本

完整代码如下:

public class ZProgressbar extends ProgressBar {    private static final String TAG = "SimpleProgressbar";    public static final int DEFAULT_UNREACHED_COLOR = 0xFF7D9EC0;    public static final int DEFAULT_REACHED_COLOR = 0xFFC1FFC1;    public static final int DEFAULT_TEXT_COLOR = 0xFF0000CD;    public static final String DEFAULT_TEXT = "100%";    // 进度条默认高,单位为 dp    public static final int DEFAULT_LINE_HEIGHT = 2;    // 进度条默认宽,单位为 dp    public static final int DEFAULT_LINE_WIDTH = 100;    // 文本大小,单位为 sp    public static final int DEFAULT_TEXT_SIZE = 8;    private Paint paint;    private Rect textBound;    private int reachedColor;    private int unreachedColor;    private int textColor;    private int lineHeight;    private int minLineHeight;    private int minLineWidth;    private int textSize;    private int textHeight;    private int textWidth;    private boolean isShowText;    public ZProgressbar(Context context) {        //        super(context);        this(context, null);    }    public ZProgressbar(Context context, AttributeSet attrs) {        //        super(context, attrs);        this(context, attrs, 0);    }    public ZProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        textBound = new Rect();        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;        textColor = DEFAULT_TEXT_COLOR;        minLineHeight = dp2px(DEFAULT_LINE_HEIGHT);        minLineWidth = dp2px(DEFAULT_LINE_WIDTH);        textSize = sp2px(DEFAULT_TEXT_SIZE);        isShowText = true;        obtainStyledAttributes(context, attrs, defStyleAttr);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        // 计算文本的宽和高        paint.setTextSize(textSize);        paint.getTextBounds(DEFAULT_TEXT, 0, DEFAULT_TEXT.length(), textBound);        textWidth = textBound.width();        textHeight = textBound.height();        int minHeight = minLineHeight;        if (isShowText) {            // 比较文本的高和线段的高            minHeight = textHeight > minLineHeight ? textHeight : minLineHeight;        }        int desiredWidth = minLineWidth + getPaddingLeft() + getPaddingRight();        int desiredHeight = minHeight + getPaddingTop() + getPaddingBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = desiredWidth;        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = desiredHeight;            lineHeight = minLineHeight;        } else {            height = Math.max(heightSize, desiredHeight);            lineHeight = height - getPaddingLeft() - getPaddingRight();        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {        //        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        if (isShowText) {            float ratio = getProgress() * 1.0f / getMax();            int unreachedWidth = (int) ((contentWidth - textWidth) * (1 - ratio));            int reachedWidth = contentWidth - textWidth - unreachedWidth;            paint.setColor(reachedColor);            paint.setStrokeWidth(lineHeight);            int startX = getPaddingLeft();            int startY = height / 2;            int stopX = getPaddingLeft() + reachedWidth;            int stopY = height / 2;            canvas.drawLine(startX, startY, stopX, stopY, paint);            String currentText = getProgress() + "%";            paint.getTextBounds(currentText, 0, currentText.length(), textBound);            paint.setColor(textColor);            paint.setTextSize(textSize);            startX = getPaddingLeft() + reachedWidth + (textWidth - textBound.width()) / 2;            Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();            startY = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;            canvas.drawText(currentText, startX, startY, paint);            paint.setColor(unreachedColor);            paint.setStrokeWidth(lineHeight);            startX = getPaddingLeft() + reachedWidth + textWidth;            startY = height / 2;            stopX = width - getPaddingRight();            stopY = height / 2;            canvas.drawLine(startX, startY, stopX, stopY, paint);        } else {            float ratio = getProgress() * 1.0f / getMax();            int unreachedWidth = (int) (contentWidth * (1 - ratio));            int reachedWidth = contentWidth - unreachedWidth;            paint.setColor(reachedColor);            paint.setStrokeWidth(lineHeight);            int startX = getPaddingLeft();            int startY = height / 2;            int stopX = getPaddingLeft() + reachedWidth;            int stopY = height / 2;            canvas.drawLine(startX, startY, stopX, stopY, paint);            paint.setColor(unreachedColor);            paint.setStrokeWidth(lineHeight);            startX = getPaddingLeft() + reachedWidth;            startY = height / 2;            stopX = width - getPaddingRight();            stopY = height / 2;            canvas.drawLine(startX, startY, stopX, stopY, paint);        }    }    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ZProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.ZProgressbar_reachedColor:                    reachedColor = a.getColor(attr, DEFAULT_REACHED_COLOR);                    break;                case R.styleable.ZProgressbar_unreachedColor:                    unreachedColor = a.getColor(attr, DEFAULT_UNREACHED_COLOR);                    break;                case R.styleable.ZProgressbar_textColor:                    textColor = a.getColor(attr, DEFAULT_TEXT_COLOR);                    break;                case R.styleable.ZProgressbar_textSize:                    textSize = sp2px((int) a.getDimension(attr, DEFAULT_TEXT_SIZE));                    break;                case R.styleable.ZProgressbar_isShowText:                    isShowText = a.getBoolean(attr, true);                    break;            }        }        a.recycle();    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }    /**     * sp 2 px     *     * @param spVal     * @return     */    protected int sp2px(int spVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                spVal, getResources().getDisplayMetrics());    }}

资源文件如下:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="reachedColor" format="color" />    <attr name="unreachedColor" format="color" />    <attr name="textSize" format="dimension" />    <attr name="textColor" format="color" />    <attr name="isShowText" format="boolean" />    <declare-styleable name="SimpleProgressbar">        <attr name="reachedColor" />        <attr name="unreachedColor" />    </declare-styleable>    <declare-styleable name="ZProgressbar">        <attr name="reachedColor" />        <attr name="unreachedColor" />        <attr name="textSize" />        <attr name="textColor" />        <attr name="isShowText" />    </declare-styleable></resources>

布局如下:

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:padding="8dp"    tools:context="com.zj.progressnumber.MainActivity">    <com.zj.progressnumber.SimpleProgressbar        android:id="@+id/spb"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="50dp"        android:background="@android:color/holo_green_light"        android:padding="8dp"        android:progress="10"        app:reachedColor="@android:color/holo_orange_light"        app:unreachedColor="@color/colorAccent" />    <com.zj.progressnumber.ZProgressbar        android:id="@+id/zpb"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="100dp"        android:background="@android:color/holo_green_light"        android:padding="8dp"        android:progress="10"        app:reachedColor="@color/colorPrimary"        app:textColor="@android:color/holo_orange_dark"        app:unreachedColor="@color/colorAccent" /></android.support.design.widget.CoordinatorLayout>

程序如下:

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final SimpleProgressbar spb = (SimpleProgressbar) findViewById(R.id.spb);        addProgress(spb);        ZProgressbar zpb = (ZProgressbar) findViewById(R.id.zpb);        addProgress(zpb);    }    private void addProgress(final ProgressBar spb) {        final int max = spb.getMax();        new Thread(new Runnable() {            @Override            public void run() {                int progress = spb.getProgress();                int random = (int) (Math.random() * 10) + 1;                random = (progress + random) <= max ? random : (max - progress);                while ((progress + random) <= max) {                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    progress = progress + random;                    spb.setProgress(progress);                    if (progress == max)                        break;                    random = (int) (Math.random() * 10) + 1;                    random = (progress + random) <= max ? random : (max - progress);                }            }        }).start();    }}

圆形进度条

画圆形进度条,需要绘制 3 个部分:圆,圆弧,文本

其中 表示进度条,圆弧 表示当前进度,文本 显示在圆中间

最简单的圆形进度条

老规矩,先实现最简单的圆形进度条:圆和圆弧。新建 RoundProgressbar.java

public class RoundProgressbar extends ProgressBar {    /**     * 画笔     */    private Paint paint;    /**     * 绘制圆弧时使用     */    private RectF rectF;    /**     * 圆半径     */    int radius;    /**     * 圆心横坐标     */    int centerX;    /**     * 圆心纵坐标     */    int centerY;    public RoundProgressbar(Context context) {//        super(context);        this(context, null);    }    public RoundProgressbar(Context context, AttributeSet attrs) {//        super(context, attrs);        this(context, attrs, 0);    }    public RoundProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        // 设置画笔宽度为 30 像素        paint.setStrokeWidth(30);        rectF = new RectF();    }    @Override    protected synchronized void onDraw(Canvas canvas) {//        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        int contentHeight = height - getPaddingTop() - getPaddingBottom();        // 设置圆心为画布正中心        radius = contentWidth >= contentHeight ? contentWidth / 2 : contentHeight / 2;        centerX = width / 2;        centerY = height / 2;        // 仅绘制边        paint.setStyle(Paint.Style.STROKE);        paint.setColor(Color.BLUE);        canvas.drawCircle(centerX, centerY, radius, paint);        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);        float ratio = getProgress() * 1.0f / getMax();        int angle = (int) (ratio * 360);        paint.setColor(Color.RED);        paint.setStyle(Paint.Style.STROKE);        canvas.drawArc(rectF, 0, angle, false, paint);    }}

布局文件:

<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:padding="8dp"    tools:context="com.zj.progressnumber.MainActivity">    <com.zj.progressnumber.RoundProgressbar        android:id="@+id/rpb"        android:layout_width="200dp"        android:layout_height="200dp"        android:background="@android:color/holo_green_light"        android:progress="50"        android:padding="20dp" /></android.support.design.widget.CoordinatorLayout>

这里写图片描述

实现:

这里写图片描述

自定义属性和尺寸测量

在上面的基础上增加画笔宽度的自定义属性,以及进行尺寸测量

public class RoundProgressbar extends ProgressBar {    // 设置默认圆大小,单位为 dp    public static final int DEFAULT_RADIUS = 30;    // 设置默认画笔宽度,单位为 dp    public static final int DEFAULT_STROKE_WIDTH = 1;    /**     * 画笔     */    private Paint paint;    /**     * 绘制圆弧时使用     */    private RectF rectF;    /**     * 圆半径     */    int radius;    /**     * 圆心横坐标     */    int centerX;    /**     * 圆心纵坐标     */    int centerY;    /**     * 画笔宽度     */    int paintWidth;    public RoundProgressbar(Context context) {//        super(context);        this(context, null);    }    public RoundProgressbar(Context context, AttributeSet attrs) {//        super(context, attrs);        this(context, attrs, 0);    }    public RoundProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        rectF = new RectF();        radius = dp2px(DEFAULT_RADIUS);        paintWidth = dp2px(DEFAULT_STROKE_WIDTH);        obtainStyledAttributes(context, attrs, defStyleAttr);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int desiredWidth = radius * 2 + getPaddingLeft() + getPaddingRight();        int desiredHeight = radius * 2 + getTop() + getBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = desiredWidth;        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = desiredHeight;        } else {            height = Math.max(heightSize, desiredHeight);        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {//        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        int contentHeight = height - getPaddingTop() - getPaddingBottom();        // 设置圆心为画布正中心        radius = contentWidth >= contentHeight ? contentHeight / 2 : contentWidth / 2;        centerX = width / 2;        centerY = height / 2;        // 仅绘制边        paint.setStyle(Paint.Style.STROKE);        paint.setColor(Color.BLUE);        paint.setStrokeWidth(paintWidth);        canvas.drawCircle(centerX, centerY, radius, paint);        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);        float ratio = getProgress() * 1.0f / getMax();        int angle = (int) (ratio * 360);        paint.setColor(Color.RED);        paint.setStyle(Paint.Style.STROKE);        canvas.drawArc(rectF, 0, angle, false, paint);    }    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.RoundProgressbar_strokeWidth:                    paintWidth = dp2px((int) a.getDimension(attr, DEFAULT_STROKE_WIDTH));                    break;            }        }        a.recycle();    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }}

属性文件:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="strokeWidth" format="dimension" />    <declare-styleable name="RoundProgressbar">        <attr name="strokeWidth" />    </declare-styleable></resources>

布局文件:

<com.zj.progressnumber.RoundProgressbar    android:id="@+id/rpb"    android:layout_width="300dp"    android:layout_height="300dp"    android:background="@android:color/holo_green_light"    android:padding="20dp"    android:progress="20"    app:strokeWidth="1dp" />

这里写图片描述

实现:

这里写图片描述

增加文本

在圆中心增加文本显示

public class RoundProgressbar extends ProgressBar {    public static final int DEFAULT_UNREACHED_COLOR = 0;    public static final int DEFAULT_REACHED_COLOR = 0;    public static final int DEFAULT_TEXT_COLOR = 0;    // 设置默认圆大小,单位为 dp    public static final int DEFAULT_RADIUS = 60;    // 设置默认画笔宽度,单位为 dp    public static final int DEFAULT_STROKE_WIDTH = 1;    // 设置文字默认大小,单位为 sp    public static final int DEFAULT_TEXT_SIZE = 12;    /**     * 画笔     */    private Paint paint;    /**     * 绘制圆弧时使用     */    private RectF rectF;    /**     * 文字大小     */    private Rect rect;    /**     * 圆半径     */    int radius;    /**     * 圆心横坐标     */    int centerX;    /**     * 圆心纵坐标     */    int centerY;    /**     * 画笔宽度     */    int paintWidth;    /**     * 文字大小     */    int textSize;    public RoundProgressbar(Context context) {//        super(context);        this(context, null);    }    public RoundProgressbar(Context context, AttributeSet attrs) {//        super(context, attrs);        this(context, attrs, 0);    }    public RoundProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        rectF = new RectF();        rect = new Rect();        radius = dp2px(DEFAULT_RADIUS);        paintWidth = dp2px(DEFAULT_STROKE_WIDTH);        textSize = sp2px(DEFAULT_TEXT_SIZE);        obtainStyledAttributes(context, attrs, defStyleAttr);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int desiredWidth = radius * 2 + getPaddingLeft() + getPaddingRight();        int desiredHeight = radius * 2 + getTop() + getBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = desiredWidth;        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = desiredHeight;        } else {            height = Math.max(heightSize, desiredHeight);        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {//        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        int contentHeight = height - getPaddingTop() - getPaddingBottom();        // 设置圆心为画布正中心        radius = contentWidth >= contentHeight ? contentHeight / 2 : contentWidth / 2;        centerX = width / 2;        centerY = height / 2;        // 仅绘制边        paint.setStyle(Paint.Style.STROKE);        paint.setColor(Color.BLUE);        paint.setStrokeWidth(paintWidth);        canvas.drawCircle(centerX, centerY, radius, paint);        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);        float ratio = getProgress() * 1.0f / getMax();        int angle = (int) (ratio * 360);        paint.setColor(Color.RED);        paint.setStyle(Paint.Style.STROKE);        canvas.drawArc(rectF, 0, angle, false, paint);        String text = getProgress() + "%";        paint.setStyle(Paint.Style.FILL);        paint.setTextSize(textSize);        paint.getTextBounds(text, 0, text.length(), rect);        int startX = centerX - rect.width() / 2;        Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();        int startY = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;        canvas.drawText(text, startX, startY, paint);    }    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.RoundProgressbar_strokeWidth:                    paintWidth = dp2px((int) a.getDimension(attr, DEFAULT_STROKE_WIDTH));                    break;                case R.styleable.RoundProgressbar_textSize:                    textSize = sp2px((int) (a.getDimension(attr, DEFAULT_TEXT_SIZE)));                    break;            }        }        a.recycle();    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }    /**     * sp 2 px     *     * @param spVal     * @return     */    protected int sp2px(int spVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                spVal, getResources().getDisplayMetrics());    }}

增加了自定义属性 - 文字大小:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="textSize" format="dimension" />    <attr name="strokeWidth" format="dimension" />    <declare-styleable name="RoundProgressbar">        <attr name="strokeWidth" />        <attr name="textSize" />    </declare-styleable></resources>

这里写图片描述

实现:

这里写图片描述

完善

目前,圆形进度条已实现进度显示和文本显示,下面增加自定义属性,可以自定义颜色

完整代码如下:

public class RoundProgressbar extends ProgressBar {    public static final int DEFAULT_UNREACHED_COLOR = 0xFF6495ED;    public static final int DEFAULT_REACHED_COLOR = 0xFFFF0000;    public static final int DEFAULT_TEXT_COLOR = 0xFF0000CD;    // 设置默认圆大小,单位为 dp    public static final int DEFAULT_RADIUS = 60;    // 设置默认画笔宽度,单位为 dp    public static final int DEFAULT_STROKE_WIDTH = 1;    // 设置文字默认大小,单位为 sp    public static final int DEFAULT_TEXT_SIZE = 12;    private Paint paint;    private RectF rectF;    private Rect rect;    int radius;    int centerX;    int centerY;    int unreachedColor;    int reachedColor;    int textColor;    int paintWidth;    int textSize;    public RoundProgressbar(Context context) {//        super(context);        this(context, null);    }    public RoundProgressbar(Context context, AttributeSet attrs) {//        super(context, attrs);        this(context, attrs, 0);    }    public RoundProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        paint = new Paint();        rectF = new RectF();        rect = new Rect();        radius = dp2px(DEFAULT_RADIUS);        paintWidth = dp2px(DEFAULT_STROKE_WIDTH);        textSize = sp2px(DEFAULT_TEXT_SIZE);        unreachedColor = DEFAULT_UNREACHED_COLOR;        reachedColor = DEFAULT_REACHED_COLOR;        textColor = DEFAULT_TEXT_COLOR;        obtainStyledAttributes(context, attrs, defStyleAttr);    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int desiredWidth = radius * 2 + getPaddingLeft() + getPaddingRight();        int desiredHeight = radius * 2 + getTop() + getBottom();        int width;        int height;        if (widthMode == MeasureSpec.AT_MOST) {            width = desiredWidth;        } else {            width = Math.max(widthSize, desiredWidth);        }        if (heightMode == MeasureSpec.AT_MOST) {            height = desiredHeight;        } else {            height = Math.max(heightSize, desiredHeight);        }        setMeasuredDimension(width, height);    }    @Override    protected synchronized void onDraw(Canvas canvas) {//        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        int contentWidth = width - getPaddingLeft() - getPaddingRight();        int contentHeight = height - getPaddingTop() - getPaddingBottom();        // 设置圆心为画布正中心        radius = contentWidth >= contentHeight ? contentHeight / 2 : contentWidth / 2;        centerX = width / 2;        centerY = height / 2;        // 仅绘制边        paint.setStyle(Paint.Style.STROKE);        paint.setColor(unreachedColor);        paint.setStrokeWidth(paintWidth);        canvas.drawCircle(centerX, centerY, radius, paint);        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);        float ratio = getProgress() * 1.0f / getMax();        int angle = (int) (ratio * 360);        paint.setColor(reachedColor);        paint.setStyle(Paint.Style.STROKE);        canvas.drawArc(rectF, 0, angle, false, paint);        String text = getProgress() + "%";        paint.setColor(textColor);        paint.setStyle(Paint.Style.FILL);        paint.setTextSize(textSize);        paint.getTextBounds(text, 0, text.length(), rect);        int startX = centerX - rect.width() / 2;        Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();        int startY = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;        canvas.drawText(text, startX, startY, paint);    }    private void obtainStyledAttributes(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundProgressbar, defStyleAttr, 0);        int count = a.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = a.getIndex(i);            switch (attr) {                case R.styleable.RoundProgressbar_strokeWidth:                    paintWidth = dp2px((int) a.getDimension(attr, DEFAULT_STROKE_WIDTH));                    break;                case R.styleable.RoundProgressbar_textSize:                    textSize = sp2px((int) (a.getDimension(attr, DEFAULT_TEXT_SIZE)));                    break;                case R.styleable.RoundProgressbar_unreachedColor:                    unreachedColor = a.getColor(attr, DEFAULT_UNREACHED_COLOR);                    break;                case R.styleable.RoundProgressbar_reachedColor:                    reachedColor = a.getColor(attr, DEFAULT_REACHED_COLOR);                    break;                case R.styleable.RoundProgressbar_textColor:                    textColor = a.getColor(attr, DEFAULT_TEXT_COLOR);                    break;            }        }        a.recycle();    }    /**     * dp 2 px     *     * @param dpVal     */    protected int dp2px(int dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dpVal, getResources().getDisplayMetrics());    }    /**     * sp 2 px     *     * @param spVal     * @return     */    protected int sp2px(int spVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,                spVal, getResources().getDisplayMetrics());    }}

资源文件:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="reachedColor" format="color" />    <attr name="unreachedColor" format="color" />    <attr name="textSize" format="dimension" />    <attr name="textColor" format="color" />    <attr name="strokeWidth" format="dimension" />    <declare-styleable name="RoundProgressbar">        <attr name="strokeWidth" />        <attr name="textSize" />        <attr name="textColor" />        <attr name="unreachedColor" />        <attr name="reachedColor" />    </declare-styleable></resources>

布局文件:

<com.zj.progressnumber.RoundProgressbar    android:id="@+id/rpb"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:background="@android:color/holo_green_light"    android:padding="20dp"/>

这里写图片描述

实现:

这里写图片描述