環(huán)形進(jìn)度條可以設(shè)置圓環(huán)寬度、圓環(huán)進(jìn)度顏色、圓環(huán)軌道顏色、圓環(huán)刷新速率、文案內(nèi)容等屬性,可實(shí)現(xiàn)更多展示效果。并將進(jìn)度條子集進(jìn)行匯總嵌套,通過設(shè)置圓環(huán)間隔呈現(xiàn)IWatch環(huán)形進(jìn)度條效果。
一、環(huán)形進(jìn)度條

環(huán)形進(jìn)度條.png
1、自定義屬性
-
在XML布局中可配置控件的屬性,實(shí)現(xiàn)自定義控件時(shí),我們需要抽出可配置的公有屬性,方便塑造更多形式。
| 字段名 | 字段類型 | 字段說明 |
|---|---|---|
| _roundColor | color | 進(jìn)度條背景色 |
| _roundProgressColor | color | 進(jìn)度填充色 |
| _roundWidth | color | 進(jìn)度畫筆寬 |
| _textColor | dimension | 進(jìn)度文字色 |
| _rate | integer | 進(jìn)度刷新速率 |
| _max | integer | 進(jìn)度最大值 |
| _progress | integer | 進(jìn)度值 |
| _textIsDisplayable | boolean | 是否顯示進(jìn)度文字 |
| _style | enum | 環(huán)形進(jìn)度條是否鏤空,STROKE,F(xiàn)ILL |
<declare-styleable name="ChildProgressBar">
<attr name="_roundColor" format="color" />
<attr name="_roundProgressColor" format="color" />
<attr name="_roundWidth" format="dimension" />
<attr name="_textColor" format="color" />
<attr name="_rate" format="integer" />
<attr name="_max" format="integer" />
<attr name="_progress" format="integer" />
<attr name="_textIsDisplayable" format="boolean" />
<attr name="_style">
<enum name="STROKE" value="0" />
<enum name="FILL" value="1" />
</attr>
</declare-styleable>
2、繪制
-
需定義每個(gè)元素的畫筆屬性和繪制位置,相互之間可以形成約束,比如圓環(huán)寬可以和文字大小和圓點(diǎn)大小形成約束,降低用戶操作難度。
-
計(jì)算字體大小跟隨進(jìn)度寬度變化
int centerX = getWidth() / 2; // 獲取圓心的x坐標(biāo)
int centerY = getHeight() / 2;
int radius;
if (centerX > centerY) {
radius = (int) (centerY - roundWidth); // 圓環(huán)的半徑 減10的目的是為了讓字體
} else {
radius = (int) (centerX - roundWidth); // 圓環(huán)的半徑 減10的目的是為了讓字體
}
lessSize = 20;
textSize = roundWidth / 2 + 35;//根據(jù)畫筆寬度改變字體大小
-
繪制底部圓軌道
paint.setColor(roundColor); // 設(shè)置圓環(huán)的顏色
paint.setStyle(Paint.Style.STROKE); // 設(shè)置空心
paint.setStrokeWidth(roundWidth); // 設(shè)置圓環(huán)的寬度
paint.setAntiAlias(true); // 消除鋸齒
canvas.drawCircle(centerX, centerY, radius, paint); // 畫出圓環(huán)
-
繪制圓心文字
if (textIsDisplayable && style == STROKE){
paint.setColor(Color.WHITE);
paint.setTextSize(textSize);
paint.setTypeface(Typeface.DEFAULT); // 設(shè)置字體
float p = 0.0f;
if (max != 0) {
p = ((float) progress / (float) max) * 100.0f;
}
DecimalFormat decimalFormat = new DecimalFormat("######0.0");//構(gòu)造方法的字符格式這里如果小數(shù)不足1位,會(huì)以0補(bǔ)足.
String percent = decimalFormat.format(p) + "%";//format 返回的是字符串
float percentWidth = paint.measureText(percent);// 測量字體寬度,我們需要根據(jù)字體的寬度設(shè)置在圓環(huán)中間
canvas.drawText(percent, centerX - percentWidth / 2, centerY, paint); // 畫出進(jìn)度百分比
paint.setTextSize(textSize); // 改變畫筆字體大小格式
String s = text + "率";
float textWidth = paint.measureText(s); // 測量字體寬度,我們需要根據(jù)字體的寬度設(shè)置在圓環(huán)中間
canvas.drawText(s, centerX - textWidth / 2, centerY + (textSize - lessSize / 2), paint); // 畫出進(jìn)度百分比
}
-
繪制帶圓角的圓弧進(jìn)度
paint.setStrokeWidth(roundWidth); // 設(shè)置圓環(huán)的寬度
paint.setColor(roundProgressColor); // 設(shè)置進(jìn)度的顏色
RectF oval = new RectF((centerX - radius), (centerY - radius),
(centerX + radius), (centerY + radius)); // 用于定義的圓弧的形狀和大小的界限
switch (style) {
case STROKE: {
paint.setStyle(Paint.Style.STROKE); // 設(shè)置進(jìn)度是實(shí)心還是空心
paint.setStrokeCap(Paint.Cap.ROUND); // 設(shè)置線冒樣式
if (max != 0) {
if (progress == 0) {
canvas.drawArc(oval, -90, 1, false, paint);
} else {
canvas.drawArc(oval, -90, (count) * 360 / max, false, paint); // 根據(jù)進(jìn)度畫圓弧
}
} else {
canvas.drawArc(oval, 0, 0, false, paint); // 根據(jù)進(jìn)度畫圓弧
}
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); // 根據(jù)進(jìn)度畫圓弧
}
} else {
canvas.drawArc(oval, 0, 0, true, paint); // 根據(jù)進(jìn)度畫圓弧
}
break;
}
}
-
繪制與圓環(huán)頂部齊平的進(jìn)度條文字描述和小圓點(diǎn)
// 繪制與圓環(huán)填充色一致的小圓點(diǎn)
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);// 設(shè)置字體
paint.setTextSize(textSize); // 改變畫筆字體大小格式
canvas.drawText(text + progress, centerX - 5 * distance,
centerY + (textSize - lessSize) / 2 - radius, paint); // 畫出進(jìn)度百分比
3、實(shí)現(xiàn)動(dòng)態(tài)刷新機(jī)制
-
利用Handler的消息延時(shí)機(jī)制,按照一定速率的增加繪制進(jìn)度,實(shí)現(xiàn)動(dòng)態(tài)刷新效果
/**
* 記錄當(dāng)前所畫的每小塊圓弧個(gè)數(shù)
*/
private int count = 0;
/**
* 記錄還沒畫出的圓弧進(jìn)度
*/
private int reverse_pro;
/**
* 圓環(huán)動(dòng)畫速度
*/
private int rate;
/**
* 設(shè)置進(jìn)度,此為線程安全控件,由于考慮多線的問題,需要同步 刷新界面調(diào)用postInvalidate()能在非UI線程刷新
*
* @param progress 當(dāng)前需繪制的進(jìn)度
*/
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;// 將傳進(jìn)來的進(jìn)程數(shù)傳給用來記錄當(dāng)前圓環(huán)的比率
postInvalidate();
}
}
public void refreshProgress() {
count = 0; // 記錄已經(jīng)繪制好的圓弧進(jìn)度,在 onDraw 中運(yùn)用
reverse_pro = progress; // 記錄還沒畫出的圓弧進(jìn)度
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來記錄弧度單元格個(gè)數(shù)
if (reverse_pro > 0) {
Message message = handler.obtainMessage(1);
handler.sendMessageDelayed(message, rate);
}
break;
}
super.handleMessage(msg);
}
};
4、部署控件
<com.xs.lightpuzzle.demo.a_circle_progress_bar_demo.ChildProgressBar
android:id="@+id/a_progress_bar"
android:layout_width="180dp"
android:layout_height="180dp"
progress_bars:_textColor="@color/white"
progress_bars:_roundColor="@color/progress_track_color"
progress_bars:_roundProgressColor="@color/progress_color"
progress_bars:_roundWidth="8dp"
progress_bars:_style="STROKE"
progress_bars:_rate="20"
progress_bars:_max="100"
progress_bars:_progress="75"
progress_bars:_textIsDisplayable="true"/>
二、進(jìn)度條嵌套集合

環(huán)形進(jìn)度條嵌套集合.png
1、自定義屬性
| 字段名 | 字段類型 | 字段說明 |
|---|---|---|
| rate | integer | 進(jìn)度刷新速率 |
| progressTrackColor | color | 進(jìn)度條底部軌道顏色 |
| textColor | color | 進(jìn)度條文字顏色 |
| progressGap | dimension | 每根進(jìn)度條彼此間隔 |
| roundWidth | dimension | 每根進(jìn)度條繪制寬度 |
| onlyFirstShowCenter | boolean | 總體是否只顯示第一條進(jìn)度的中心文案 |
| isShowTopTextAndPoint | boolean | 每條進(jìn)度是否顯示頂部齊平的文案和圓點(diǎn) |
<declare-styleable name="ProgressBarsView">
<attr name="rate" format="integer" />
<attr name="progressTrackColor" format="color" />
<attr name="textColor" format="color" />
<attr name="progressGap" format="dimension"/>
<attr name="roundWidth" format="dimension"/>
<attr name="onlyFirstShowCenter" format="boolean" />
<attr name="isShowTopTextAndPoint" format="boolean" />
</declare-styleable>
public ProgressBarsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressBarsView);
// 獲取自定義屬性和默認(rèn)值
progressTrackColor = mTypedArray.getColor(R.styleable.ProgressBarsView_progressTrackColor, Color.GRAY); // 每條進(jìn)度底部軌道顏色一致
textColor = mTypedArray.getColor(R.styleable.ProgressBarsView_textColor, Color.WHITE); // 每條進(jìn)度文字顏色一致
rate = mTypedArray.getInteger(R.styleable.ProgressBarsView_rate, 50); // 每條進(jìn)度刷新速率一致
progressGap = mTypedArray.getDimension(R.styleable.ProgressBarsView_progressGap, 10); // 每條進(jìn)度彼此間隔一致
roundWidth = mTypedArray.getDimension(R.styleable.ProgressBarsView_roundWidth, 20); // 每條進(jìn)度寬度一致
onlyFirstShowCenter = mTypedArray.getBoolean(R.styleable.ProgressBarsView_onlyFirstShowCenter, true); // 總體是否只顯示第一條進(jìn)度的中心文案
isShowTopTextAndPoint = mTypedArray.getBoolean(R.styleable.ProgressBarsView_isShowTopTextAndPoint, true); // 每條進(jìn)度是否顯示頂部齊平的文案和圓點(diǎn)
}
2、控件部署
<com.xs.lightpuzzle.demo.a_circle_progress_bar_demo.ProgressBarsView
android:id="@+id/team_diary_data_progress_rate_bar"
android:layout_width="180dp"
android:layout_height="180dp"
progress_bars:rate="20"
progress_bars:roundWidth="8dp"
progress_bars:progressGap="3dp"
progress_bars:onlyFirstShowCenter="true"
progress_bars:isShowTopTextAndPoint="true"
progress_bars:textColor="@color/progress_text_color"
progress_bars:progressTrackColor="@color/progress_track_color"/>
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); // 是否繪制與圓頂部平行的文字和圓點(diǎn)
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環(huán)形進(jìn)度條自定義控件
progressbar.addProgressBar("勝", 0XBBEA595C, 100, 80);
progressbar.addProgressBar("平", 0XBBD6A20E, 100, 90);
progressbar.addProgressBar("負(fù)", 0XBBD7D5DA, 100, 30);
progressbar.showProgress();

仿IWatch環(huán)形進(jìn)度條嵌套集合.png

IWatch環(huán)形進(jìn)度條控件.jpg