拿到美工效果图,咱们程序员就得画得一模一样。 为了不被老板喷,只能多练啊。
听说你觉得前面几篇都so easy,那今天就带你做个相对比较复杂的。
今天的效果图如下(左边是ui图 右边是实现图):
自我感觉总体效果还不错,至少大概画得一样了。上一个动态图:
其实这个效果实现起来也不是很难,就是计算坐标,弧度之类的可能会比较麻烦,这里分享写这个其中一张手稿,请无视掉很丑的字,其实做自定义view 还是要在纸上多画。所以希望大家也能这么画画,思路会很顺。
好的了,废话不多说,快开始。
首先自定义属性 构造函数,测量什么的 你肯定已经很熟练 直接贴代码了,注释写的很清楚
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
|
public
class
PanelView
extends
View {
private
int
mWidth;
private
int
mHeight;
private
int
mPercent;
//刻度宽度
private
float
mTikeWidth;
//第二个弧的宽度
private
int
mScendArcWidth;
//最小圆的半径
private
int
mMinCircleRadius;
//文字矩形的宽
private
int
mRectWidth;
//文字矩形的高
private
int
mRectHeight;
//文字内容
private
String mText =
""
;
//文字的大小
private
int
mTextSize;
//设置文字颜色
private
int
mTextColor;
private
int
mArcColor;
//小圆和指针颜色
private
int
mMinCircleColor;
//刻度的个数
private
int
mTikeCount;
private
Context mContext;
public
PanelView(Context context) {
this
(context,
null
);
}
public
PanelView(Context context, AttributeSet attrs) {
this
(context, attrs,
0
);
}
public
PanelView(Context context, AttributeSet attrs,
int
defStyleAttr) {
super
(context, attrs, defStyleAttr);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.PanelView,defStyleAttr,
0
);
mArcColor = a.getColor(R.styleable.PanelView_arcColor, Color.parseColor(
"#5FB1ED"
));
mMinCircleColor = a.getColor(R.styleable.PanelView_pointerColor,Color.parseColor(
"#C9DEEE"
));
mTikeCount = a.getInt(R.styleable.PanelView_tikeCount,
12
);
mTextSize = a.getDimensionPixelSize(PxUtils.spToPx(R.styleable.PanelView_android_textSize,mContext),
24
);
mText = a.getString(R.styleable.PanelView_android_text);
mScendArcWidth =
50
;
}
@Override
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
int
widthSize = MeasureSpec.getSize(widthMeasureSpec);
int
widthMode = MeasureSpec.getMode(widthMeasureSpec);
int
heightSize = MeasureSpec.getSize(heightMeasureSpec);
int
heightMode = MeasureSpec.getMode(heightMeasureSpec);
if
(widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize;
}
else
{
mWidth = PxUtils.dpToPx(
200
,mContext);
}
if
(heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
}
else
{
mHeight = PxUtils.dpToPx(
200
,mContext);
}
Log.e(
"wing"
,mWidth+
""
);
setMeasuredDimension(mWidth, mHeight);
}
|
1
2
3
4
5
6
7
8
9
10
11
|
<!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%
221.0
%
22
%20encoding%3D%22utf-
8
%
22
%3F%2D%2D%3E-->
<resources>
<declare-styleable name=
"PanelView"
>
<attr name=
"arcColor"
format=
"color"
>
<attr name=
"arcWidth"
format=
"dimension"
>
<attr name=
"android:text"
>
<attr name=
"tikeCount"
format=
"integer"
>
<attr name=
"pointerColor"
format=
"color"
>
<attr name=
"android:textSize"
>
</attr></attr></attr></attr></attr></attr></declare-styleable>
</resources>
|
之后来重头戏,也就是绘制。就像画画一样,再复杂的view也是一笔一笔画出来的。所以我们把这个view分解。
大概分解成如下:1.最外面的弧 2.里面的粗弧 3.中间小圆 4.最小的圆 5.刻度 6.指针 7.矩形 8.文字
相信让你分开画一定难不倒你。那组合在一起 就是这个view啦。下面开始我们的ondraw()
按照这个分解来:
1.绘制最外面的弧 这里需要注意的一点是,如果想让这个圆在view里 记得减去画笔宽度的一半 因为半径是从圆心到画笔宽度的中间算的,所以这里画弧的矩形是 new RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth)
1
2
3
4
5
6
7
8
|
Paint p =
new
Paint();
int
strokeWidth =
3
;
p.setStrokeWidth(strokeWidth);
p.setAntiAlias(
true
);
p.setStyle(Paint.Style.STROKE);
p.setColor(mArcColor);
//最外面线条
canvas.drawArc(
new
RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth),
145
,
250
,
false
, p);
|
画出来是这样的效果。
![\](http://www.2cto.com/uploadfile/Collfiles/20160107/2016010709384676.jpg)
2.绘制里面的粗弧,这里比较麻烦的就是需要分为四段,看图:
因为大圆和里面粗弧的长短不一致,这里使用百分比来计算 所以会造成指针偏差,那么这里把 1、2两个部分固定来画,然后是3 充满的部分,用百分比来计算需要画多少度,最后是4 空白的部分。
首先把粗弧的矩形画出来,这里固定了比大弧半径少50(这里其实可以改进,你可以改成动态的让他更灵活),然后计算出百分比。
1
2
3
4
|
RectF secondRectF =
new
RectF(strokeWidth +
50
, strokeWidth +
50
, mWidth - strokeWidth -
50
, mHeight - strokeWidth -
50
);
float
secondRectWidth = mWidth - strokeWidth -
50
- (strokeWidth +
50
);
float
secondRectHeight = mHeight - strokeWidth -
50
- (strokeWidth +
50
);
float
percent = mPercent / 100f;
|
接下来绘制1弧,先算出fill充满部分的度数,因为是突出的,所以如果百分比为0,突出左端为白色 如果不为零,则和充满颜色统一。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//充满的圆弧的度数 -5是大小弧的偏差
float
fill =
250
* percent ;
//空的圆弧的度数
float
empty =
250
- fill;
// Log.e("wing", fill + "");
if
(percent==
0
){
p.setColor(Color.WHITE);
}
//画粗弧突出部分左端
canvas.drawArc(secondRectF,
135
,
11
,
false
,p);
|
然后绘制2弧 也就是fill充满的弧,
1
|
canvas.drawArc(secondRectF,
145
, fill,
false
, p);
|
接下来是3弧,也就是empty未充满的弧,是白色的
1
2
3
|
p.setColor(Color.WHITE);
//画弧胡的未充满部分
canvas.drawArc(secondRectF,
145
+ fill, empty,
false
, p);
|
最后,画出右边突出的4弧, 如果百分比为100 那么和充满的颜色一致,否则为白色
1
2
3
4
5
|
//画粗弧突出部分右端
if
(percent ==
1
){
p.setColor(mArcColor);
}
canvas.drawArc(secondRectF,
144
+fill+empty,
10
,
false
,p);
|
这样粗弧也就画完了 来看看效果,就画了两条弧线(实际是5条),就成型了。
3.中间的小圆外圈,他的圆心不用多说 是整个view的中心
1
2
3
4
5
6
|
p.setColor(mArcColor);
//绘制小圆外圈
p.setStrokeWidth(
3
);
canvas.drawCircle(mWidth /
2
, mHeight /
2
,
30
, p);
|
4.绘制内圆,圆心一样的,半径和画笔粗度改变一下
1
2
3
4
5
6
|
//绘制小圆内圈
p.setColor(mMinCircleColor);
p.setStrokeWidth(
8
);
mMinCircleRadius =
15
;
canvas.drawCircle(mWidth /
2
, mHeight /
2
, mMinCircleRadius, p);
|
![\](http://www.2cto.com/uploadfile/Collfiles/20160107/2016010709384680.jpg)
5.刻度 刻度处理起来可能比较麻烦,用三角函数算坐标啊 循环画出来。。 这里提供一种比较简单的方法:旋转画布。
首先引入一个概念,什么叫旋转画布呢,就是把你的画布旋转。。经过测试,旋转以后,整个坐标轴都会对应旋转,一张图举例说明下。
大概就是这个意思,画布旋转之后 坐标系也就旋转了,但是原来的图像还在,所以说你比如这个点 x,y旋转前在这个位置, 那么旋转后就是另外一个位置了,但是他们的坐标是相同的。 所以刻度也可以考这种方法画。我们只要画出最顶端的刻度 然后旋转就可以了。
绘制第一段刻度, 然后总共是250的弧度 计算出每个刻度的度数 用250除以刻度数mTikeCount,就是每次旋转的度数。接下来把画布逐步旋转,按照原坐标绘制,即可绘制出右半部分刻度。 注意:为了让之后的绘制正常,务必把画布转回原来的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//绘制刻度!
p.setColor(mArcColor);
//绘制第一条最上面的刻度
mTikeWidth =
20
;
p.setStrokeWidth(
3
);
canvas.drawLine(mWidth /
2
,
0
, mWidth /
2
, mTikeWidth, p);
//旋转的角度
float
rAngle = 250f / mTikeCount;
//通过旋转画布 绘制右面的刻度
for
(
int
i =
0
; i < mTikeCount /
2
; i++) {
canvas.rotate(rAngle, mWidth /
2
, mHeight /
2
);
canvas.drawLine(mWidth /
2
,
0
, mWidth /
2
, mTikeWidth, p);
}
//现在需要将将画布旋转回来
canvas.rotate(-rAngle * mTikeCount /
2
, mWidth /
2
, mHeight /
2
);
|
![\](http://www.2cto.com/uploadfile/Collfiles/20160107/2016010709384683.jpg)
左半部分同理,需要改变的度数为负 就好了
1
2
3
4
5
6
7
8
9
|
//通过旋转画布 绘制左面的刻度
for
(
int
i =
0
; i < mTikeCount /
2
; i++) {
canvas.rotate(-rAngle, mWidth /
2
, mHeight /
2
);
canvas.drawLine(mWidth /
2
,
0
, mWidth /
2
, mTikeWidth, p);
}
//现在需要将将画布旋转回来
canvas.rotate(rAngle * mTikeCount /
2
, mWidth /
2
, mHeight /
2
);
|
![\](http://www.2cto.com/uploadfile/Collfiles/20160107/2016010709384685.jpg)
6.指针 指针的绘制和刻度相似,先算出来百分比所占的度数 然后根据 是否大于50%来旋转画布。
指针的起终点是 总view高度的一半 粗弧矩形的一半 加上小圆,前面坐标讲解了那么,这个也一样,自己拿起笔算一算。
注意这里画布旋转我通过计算得出一个公式 250 * percent - 250/2。
如果小于50% 则为负 如果大于50%则为正,然后进行旋转。
切忌最后一定要将画布转回来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//绘制指针
p.setColor(mMinCircleColor);
p.setStrokeWidth(
4
);
//按照百分比绘制刻度
canvas.rotate((
250
* percent -
250
/
2
), mWidth /
2
, mHeight /
2
);
canvas.drawLine(mWidth /
2
, (mHeight /
2
- secondRectHeight /
2
) + mScendArcWidth /
2
+
2
, mWidth /
2
, mHeight /
2
- mMinCircleRadius, p);
//将画布旋转回来
canvas.rotate(-(
250
* percent -
250
/
2
), mWidth /
2
, mHeight /
2
);
|
![\](http://www.2cto.com/uploadfile/Collfiles/20160107/2016010709384686.jpg)
接下来就是画矩形和文字。没什么好说的了,坐标也是X周围mWidth/2 y轴自己根据圆心微调一个距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//绘制矩形
p.setStyle(Paint.Style.FILL);
p.setColor(mArcColor);
mRectWidth =
60
;
mRectHeight =
25
;
//文字矩形的最底部坐标
float
rectBottomY = mHeight/
2
+ secondRectHeight/
3
+mRectHeight;
canvas.drawRect(mWidth/
2
-mRectWidth/
2
,mHeight/
2
+ secondRectHeight/
3
,mWidth/
2
+mRectWidth/
2
,rectBottomY,p);
p.setTextSize(mTextSize);
mTextColor = Color.WHITE;
p.setColor(mTextColor);
float
txtLength = p.measureText(mText);
canvas.drawText(mText,(mWidth-txtLength)/
2
,rectBottomY +
40
,p);
super
.onDraw(canvas);
|
这样完成了整个view的绘制。
下面要做的就是为了方便使用者,提供一些设置属性的方法。
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
|
/**
* 设置百分比
* @param percent
*/
public
void
setPercent(
int
percent) {
mPercent = percent;
invalidate();
}
/**
* 设置文字
* @param text
*/
public
void
setText(String text){
mText = text;
invalidate();
}
/**
* 设置圆弧颜色
* @param color
*/
public
void
setArcColor(
int
color){
mArcColor = color;
invalidate();
}
/**
* 设置指针颜色
* @param color
*/
public
void
setPointerColor(
int
color){
mMinCircleColor = color;
invalidate();
}
/**
* 设置文字大小
* @param size
*/
public
void
setTextSize(
int
size){
mTextSize = size;
invalidate();
}
/**
* 设置粗弧的宽度
* @param width
*/
public
void
setArcWidth(
int
width){
mScendArcWidth = width;
invalidate();
}
|
大功告成!!!一个看似复杂的view 经过我们一步一步绘制遍完成了。
其实技术的养成也是这样,只要一步一步脚踏实地的去练习,我相信总有一天我能成为大神,并且找到心仪的工作。