一. 前言

这个控件也可以说是为了做而做,是因为我在开发我的软件工程课程设计-连连看的时候,需要用到这个控件。

Github地址:CircleProgress

二. 自定义View

1. 自定义用到的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//进度背景的画笔
private Paint progress_bg_paint;

//进度的画笔
private Paint progress_paint;

//进度值的画笔
private Paint progress_value_paint;

//视图的布局
private RelativeLayout layout;

//进度值
private float progress = 0;

//总的进度值
private float total_progress = 100;

//开始的角度
private int startAngle = 0;

//结束的角度
private int endAngle = 360;

//字体的大小
private int textSize = 14;

//字体的颜色
private String textColor = "#ffffff";

//绘制字体的左间距
private int textLeftPadding = 0;

2. 在values文件夹下自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleProgress">
<!--当前进度值-->
<attr name="progress" format="float"/>
<!--总的进度值-->
<attr name="total_progress" format="float"/>
<!--进度开始的角度-->
<attr name="startAngle" format="integer"/>
<!--进度结束的角度-->
<attr name="endAngle" format="integer"/>
<!--文本字体的大小 sp-->
<attr name="textSize" format="integer"/>
<!--文本字体的颜色-->
<attr name="textColor" format="string"/>
<!--文本字体绘制的左间距-->
<attr name="textLeftPadding" format="integer"/>
</declare-styleable>
</resources>

3. 创建构造方法

主要还是重写两个构造方法,需要解析属性的解析属性,最终都是要存储设置的值。

不管是哪一个构造方法都是要加载布局以及对三个画笔进行初始化操作。

我们重点看下绘制文本的时候如何设置自己指定的字体文件, 参考:安卓开发引用ttf字体文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/root">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/time"
/>

</RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* 构造方法 Java代码创建进入
* @param context
*/
public CircleProgress(Context context,int startAngle,int endAngle,int total_progress) {
super(context);

//保存值
this.startAngle = startAngle;
this.endAngle = endAngle;
this.total_progress = total_progress;

//初始化
init();
}

/**
* 构造方法 Xml代码创建进入
* @param context
* @param attrs
*/
public CircleProgress(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

//解析属性
if (attrs != null){
//1.获取所有自定义属性值的集合
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgress);

//2.依次解析
progress = typedArray.getFloat(
R.styleable.CircleProgress_progress,
progress
);
setProgress(progress);
total_progress = typedArray.getFloat(
R.styleable.CircleProgress_total_progress,
total_progress
);
startAngle = typedArray.getInteger(
R.styleable.CircleProgress_startAngle,
startAngle
);
endAngle = typedArray.getInteger(
R.styleable.CircleProgress_endAngle,
endAngle
);
textSize = typedArray.getInteger(
R.styleable.CircleProgress_textSize,
textSize
);
String temp_textColor = typedArray.getString(
R.styleable.CircleProgress_textColor
);
if (temp_textColor != null) {
textColor = temp_textColor;
}
textLeftPadding = typedArray.getInteger(
R.styleable.CircleProgress_textLeftPadding,
0
);

//3.释放资源
typedArray.recycle();
}

//初始化
init();
}

/**
* 初始化
*/
private void init(){
//进度的背景画笔
progress_bg_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
progress_bg_paint.setColor(Color.parseColor("#9EE5A4"));
progress_bg_paint.setStrokeWidth(PxUtil.dpToPx(10,getContext()));
progress_bg_paint.setStyle(Paint.Style.STROKE);
progress_bg_paint.setStrokeJoin(Paint.Join.ROUND);
progress_bg_paint.setStrokeCap(Paint.Cap.ROUND);

//进度的画笔
progress_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
progress_paint.setColor(Color.parseColor("#CCBBBBBB"));
progress_paint.setStrokeWidth(PxUtil.spToPx(10,getContext()));
progress_paint.setStyle(Paint.Style.STROKE);
progress_paint.setStrokeJoin(Paint.Join.ROUND);
progress_paint.setStrokeCap(Paint.Cap.ROUND);

//进度值的画笔
progress_value_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
progress_value_paint.setColor(Color.parseColor(textColor));
progress_value_paint.setTextSize(PxUtil.dpToPx(textSize,getContext()));
progress_value_paint.setTypeface(
Typeface.createFromAsset(
getContext().getAssets(),
"font/造字工房乐真体.ttf"
)
);

//加载视图
View inflate = View.inflate(getContext(), R.layout.circle_progress, this);
layout = inflate.findViewById(R.id.root);
}

4. 绘制进度背景,进度以及进度文本

在这里,由于为了方便我的连连看,所以只考虑了连连看中的情况。时间是从90秒不停地减少的,所以如果是不断的增加的,代码需要重构。

由于progress是float,我们使用DecimalFormat类来规范其小数位数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);

//1.确定绘制的矩形区域
@SuppressLint("DrawAllocation") RectF rectF = new RectF(
layout.getLeft(),
layout.getTop(),
layout.getRight(),
layout.getBottom()
);
Log.d("CircleProgress",rectF.toString());

//2.绘制背景
canvas.drawArc(
rectF,0,
360,
false,
progress_bg_paint
);

//3.绘制进度
canvas.drawArc(
rectF,
startAngle,
(endAngle-startAngle)*((total_progress-progress)/total_progress),
false,
progress_paint
);

//4.绘制进度值
String text = new DecimalFormat("##0.0").format(progress)+"秒";
//4.1计算文本宽度
float text_width = progress_value_paint.measureText(text);
//4.2获取字体fontMetrics
Paint.FontMetrics fontMetrics = progress_value_paint.getFontMetrics();
//4.3计算文本高度
float text_height = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
//4.4绘制需要的字体
canvas.drawText(
text,
PxUtil.dpToPx(textLeftPadding,getContext())+getPivotX()-text_width/2,
getPivotY()+text_height,
progress_value_paint
);
}

public void setProgress(float progress) {
this.progress = progress;

//刷新,重绘
invalidate();
}

5. 默认的资源文件

这里控制的就是布局文件中TextView的样式,其width和height对整体呈现效果有影响。默认效果如下,但是该文件还不可以自定义,目前版本还没有自定义相关属性。如果以后需要再次使用该控件,会再次优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">

<solid android:color="#E48093"
/>

<stroke android:width="8dp" android:color="#EAB3BE"
/>

<size android:width="120dp"
android:height="120dp"/>

</shape>

三. 具体的使用

首先需要添加依赖,添加依赖之后,在布局文件中这样调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".MainActivity">

<swu.xl.circleprogress.CircleProgress
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
app:total_progress="100"
app:progress="20.0000"
app:startAngle="270"
app:endAngle="450"
app:textColor="#ffffff"
app:textSize="22"
app:textLeftPadding="10"
/>

</RelativeLayout>