参考:
Android 打造形形色色的进度条 实现可以如此简单
daimajia/NumberProgressBar
自定义视图(View
)是 Android
开发的一个进阶内容。随着开发的深入,肯定会出现系统提供的基础控件不符合需求的情况。一方面通过组合基础控件以形成新的布局,另一方面可以通过自定义控件的方式来更加灵活的实现需求
自定义视图涉及到 Android
系统许多方面的内容,下面根据自己的理解顺序来讲一讲如何自定义视图
参考: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"/>
实现: