- 一、环形进度条
- 1、自定义属性
- 2、绘制
- 3、实现动态刷新机制
- 4、部署控件
- 二、进度条嵌套集合
- 三、一键部署IWatch环形进度条自定义控件
环形进度条可以设置圆环宽度、圆环进度颜色、圆环轨道颜色、圆环刷新速率、文案内容等属性,可实现更多展示效果。并将进度条子集进行汇总嵌套,通过设置圆环间隔呈现IWatch环形进度条效果。
一、环形进度条
1、自定义属性
-
在XML布局中可配置控件的属性,实现自定义控件时,我们需要抽出可配置的公有属性,方便塑造更多形式。
字段名 |
字段类型 |
字段说明 |
_roundColor |
color |
进度条背景色 |
_roundProgressColor |
color |
进度填充色 |
_roundWidth |
color |
进度画笔宽 |
_textColor |
dimension |
进度文字色 |
_rate |
integer |
进度刷新速率 |
_max |
integer |
进度最大值 |
_progress |
integer |
进度值 |
_textIsDisplayable |
boolean |
是否显示进度文字 |
_style |
enum |
环形进度条是否镂空,STROKE,FILL |
2、绘制
-
需定义每个元素的画笔属性和绘制位置,相互之间可以形成约束,比如圆环宽可以和文字大小和圆点大小形成约束,降低用户操作难度。
-
计算字体大小跟随进度宽度变化
int centerX = getWidth() / 2; // 获取圆心的x坐标
int centerY = getHeight() / 2;
int radius;
if (centerX > centerY) {
radius = (int) (centerY - roundWidth); // 圆环的半径 减10的目的是为了让字体
} else {
radius = (int) (centerX - roundWidth); // 圆环的半径 减10的目的是为了让字体
}
lessSize = 20;
textSize = roundWidth / 2 + 35;//根据画笔宽度改变字体大小
paint.setColor(roundColor); // 设置圆环的颜色
paint.setStyle(Paint.Style.STROKE); // 设置空心
paint.setStrokeWidth(roundWidth); // 设置圆环的宽度
paint.setAntiAlias(true); // 消除锯齿
canvas.drawCircle(centerX, centerY, radius, paint); // 画出圆环
if (textIsDisplayable && style == STROKE){
paint.setColor(Color.WHITE);
paint.setTextSize(textSize);
paint.setTypeface(Typeface.DEFAULT); // 设置字体
float p = 0.0f;
if (max != 0) {
p = ((float) progress / (float) max) * 100.0f;
}
DecimalFormat decimalFormat = new DecimalFormat("######0.0");//构造方法的字符格式这里如果小数不足1位,会以0补足.
String percent = decimalFormat.format(p) + "%";//format 返回的是字符串
float percentWidth = paint.measureText(percent);// 测量字体宽度,我们需要根据字体的宽度设置在圆环中间
canvas.drawText(percent, centerX - percentWidth / 2, centerY, paint); // 画出进度百分比
paint.setTextSize(textSize); // 改变画笔字体大小格式
String s = text + "率";
float textWidth = paint.measureText(s); // 测量字体宽度,我们需要根据字体的宽度设置在圆环中间
canvas.drawText(s, centerX - textWidth / 2, centerY + (textSize - lessSize / 2), paint); // 画出进度百分比
}
paint.setStrokeWidth(roundWidth); // 设置圆环的宽度
paint.setColor(roundProgressColor); // 设置进度的颜色
RectF oval = new RectF((centerX - radius), (centerY - radius),
(centerX + radius), (centerY + radius)); // 用于定义的圆弧的形状和大小的界限
switch (style) {
case STROKE: {
paint.setStyle(Paint.Style.STROKE); // 设置进度是实心还是空心
paint.setStrokeCap(Paint.Cap.ROUND); // 设置线冒样式
if (max != 0) {
if (progress == 0) {
canvas.drawArc(oval, -90, 1, false, paint);
} else {
canvas.drawArc(oval, -90, (count) * 360 / max, false, paint); // 根据进度画圆弧
}
} else {
canvas.drawArc(oval, 0, 0, false, paint); // 根据进度画圆弧
}
break;
}
case FILL: {
paint.setStyle(Paint.Style.FILL_AND_STROKE);
if (max != 0) {
if (progress == 0) {
canvas.drawArc(oval, -90, 1, true, paint);
} else {
canvas.drawArc(oval, -90, (count) * 360 / max, true, paint); // 根据进度画圆弧
}
} else {
canvas.drawArc(oval, 0, 0, true, paint); // 根据进度画圆弧
}
break;
}
}
// 绘制与圆环填充色一致的小圆点
paint.setColor(roundProgressColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(roundWidth / 2);
canvas.drawCircle(centerX - 6 * distance, centerY - radius + distance / 2, distance / 3, paint);
// 绘制文字描述
paint.setColor(textColor);// 设置字体
paint.setTextSize(textSize); // 改变画笔字体大小格式
canvas.drawText(text + progress, centerX - 5 * distance,
centerY + (textSize - lessSize) / 2 - radius, paint); // 画出进度百分比
3、实现动态刷新机制
-
利用Handler的消息延时机制,按照一定速率的增加绘制进度,实现动态刷新效果
/**
* 记录当前所画的每小块圆弧个数
*/
private int count = 0;
/**
* 记录还没画出的圆弧进度
*/
private int reverse_pro;
/**
* 圆环动画速度
*/
private int rate;
/**
* 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步 刷新界面调用postInvalidate()能在非UI线程刷新
*
* @param progress 当前需绘制的进度
*/
public synchronized void setProgress(int progress) {
if (progress < 0) {
throw new IllegalArgumentException("progress not less than 0");
}
if (progress > max) {
progress = max;
}
if (progress <= max) {
this.progress = progress;
count = 0;
reverse_pro = progress;// 将传进来的进程数传给用来记录当前圆环的比率
postInvalidate();
}
}
public void refreshProgress() {
count = 0; // 记录已经绘制好的圆弧进度,在 onDraw 中运用
reverse_pro = progress; // 记录还没画出的圆弧进度
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, rate);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
if (reverse_pro != 0)
count++;
reverse_pro--;
postInvalidate();// progress每增加一格就刷新一次界面,用count来记录弧度单元格个数
if (reverse_pro > 0) {
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, rate);
}
break;
}
super.handleMessage(msg);
}
};
4、部署控件
二、进度条嵌套集合
1、自定义属性
字段名 |
字段类型 |
字段说明 |
rate |
integer |
进度刷新速率 |
progressTrackColor |
color |
进度条底部轨道颜色 |
textColor |
color |
进度条文字颜色 |
progressGap |
dimension |
每根进度条彼此间隔 |
roundWidth |
dimension |
每根进度条绘制宽度 |
onlyFirstShowCenter |
boolean |
总体是否只显示第一条进度的中心文案 |
isShowTopTextAndPoint |
boolean |
每条进度是否显示顶部齐平的文案和圆点 |
public ProgressBarsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressBarsView);
// 获取自定义属性和默认值
progressTrackColor = mTypedArray.getColor(R.styleable.ProgressBarsView_progressTrackColor, Color.GRAY); // 每条进度底部轨道颜色一致
textColor = mTypedArray.getColor(R.styleable.ProgressBarsView_textColor, Color.WHITE); // 每条进度文字颜色一致
rate = mTypedArray.getInteger(R.styleable.ProgressBarsView_rate, 50); // 每条进度刷新速率一致
progressGap = mTypedArray.getDimension(R.styleable.ProgressBarsView_progressGap, 10); // 每条进度彼此间隔一致
roundWidth = mTypedArray.getDimension(R.styleable.ProgressBarsView_roundWidth, 20); // 每条进度宽度一致
onlyFirstShowCenter = mTypedArray.getBoolean(R.styleable.ProgressBarsView_onlyFirstShowCenter, true); // 总体是否只显示第一条进度的中心文案
isShowTopTextAndPoint = mTypedArray.getBoolean(R.styleable.ProgressBarsView_isShowTopTextAndPoint, true); // 每条进度是否显示顶部齐平的文案和圆点
}
2、控件部署
public void addProgressBar(String text, int progressColor, int maxProgress, int progress) {
ViewHolder holder = new ViewHolder();
holder.rate = rate;
holder.roundWidth = roundWidth;
holder.progressTrackColor = progressTrackColor;
holder.isShowTopTextAndPoint = isShowTopTextAndPoint;
holder.textColor = textColor;
holder.max = maxProgress;
holder.progress = progress;
holder.text = text;
holder.roundProgressColor = progressColor;
list.add(holder);
}
public void showProgress() {
this.removeAllViews();
for (int i = 0; i < list.size(); i++) {
ViewHolder holder = list.get(i);
ChildProgressBar progressBar = new ChildProgressBar(mContext);
progressBar.setText(holder.text);
progressBar.setTextColor(holder.textColor);
progressBar.setCricleColor(holder.progressTrackColor);
progressBar.setCricleProgressColor(holder.roundProgressColor);
progressBar.setRoundWidth(holder.roundWidth);
progressBar.setRate(holder.rate);
progressBar.setMax(holder.max);
progressBar.setProgress(holder.progress);
progressBar.setTextIsDisplayable(i == 0 && onlyFirstShowCenter); // 是否绘制圆中心文案
progressBar.setShowTextHint(holder.isShowTopTextAndPoint); // 是否绘制与圆顶部平行的文字和圆点
float totalMargin = (roundWidth + progressGap) * i;
LayoutParams params = new LayoutParams(
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
params.setMargins((int) totalMargin, (int) totalMargin,
(int) totalMargin, (int) totalMargin);
this.addView(progressBar, params);
progressBar.updateBar();
}
}
三、一键部署IWatch环形进度条自定义控件
progressbar.addProgressBar("胜", 0XBBEA595C, 100, 80);
progressbar.addProgressBar("平", 0XBBD6A20E, 100, 90);
progressbar.addProgressBar("负", 0XBBD7D5DA, 100, 30);
progressbar.showProgress();