个人开发中自定义 View 系列(有需要的可以点击查看收藏)

  1. Android 自定义 view 第一弹(防小米计步)
  2. Android 自定义 View 第二弹(旋转的体重)
  3. Android 自定义 View 第三弹(反人类尺子)
  4. Android 自定义 View 第四弹(Kotlin 流式布局)

废话不多说,先上效果图:

这里写图片描述

右上脚的计步(有动画效果)

从来不说废话,上代码:

一。自定义 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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/**
* 计步View
* Created by DYJ on 2017/8/3.
*/
public class RunningView extends View {
/**
* 圆弧的宽度
*/
private float borderWidth = 20f;
/**
* 画步数的数值的字体大小
*/
private float numberTextSize = 0;
/**
* 步数
*/
private String stepNumber = "0";
/**
* 开始绘制圆弧的角度
*/
private float startAngle = -90;
/**
* 终点对应的角度和起始点对应的角度的夹角
*/
private float angleLength = 360;
/**
* 所要绘制的当前步数的红色圆弧终点到起点的夹角
*/
private float currentAngleLength = 0;
/**
* 动画时长
*/
private int animationLength = 1000;

public RunningView(Context context) {
super(context);
}

public RunningView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RunningView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**中心点的x坐标*/
float centerX = (getWidth()) / 2;
/**指定圆弧的外轮廓矩形区域*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
/**【第一步】绘制整体的黄色圆弧*/
drawArcGray(canvas, rectF);
/**【第二步】绘制当前进度的蓝色圆弧*/
drawArcRed(canvas, rectF);
/**【第三步】绘制"步数"的红色数字*/
drawTextStepString(canvas, centerX);
/**【第四步】绘制当前进度的红色数字*/
drawTextNumber(canvas, centerX);
// canvas.restore();
}

/**
* 1.绘制总步数的灰色圆弧
*
* @param canvas 画笔
* @param rectF 参考的矩形
*/
private void drawArcGray(Canvas canvas, RectF rectF){
Paint paint=new Paint();
paint.setColor(getResources().getColor(R.color.running));
/** 结合处为圆弧*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL :填充内部;Paint.Style.FILL_AND_STROKE :填充内部和描边; Paint.Style.STROKE :仅描边*/
paint.setStyle(Paint.Style.STROKE);
/**抗锯齿功能*/
paint.setAntiAlias(true);
/**设置画笔宽度*/
paint.setStrokeWidth(borderWidth);
/**绘制圆弧的方法
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,
参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线,
参数五是Paint对象;
*/
canvas.drawArc(rectF,startAngle,angleLength,false,paint);
}

/**
* 2.绘制当前步数的绿色圆弧
*/
private void drawArcRed(Canvas canvas, RectF rectF){
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//圆角弧度
paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式
paintCurrent.setAntiAlias(true);//抗锯齿功能
paintCurrent.setStrokeWidth(borderWidth);//设置画笔宽度
paintCurrent.setColor(getResources().getColor(R.color.running_green));//设置画笔颜色
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}
/**
* 3.圆环中心的步数
*/
private void drawTextNumber(Canvas canvas, float centerX){
Paint vTextPaint=new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//抗锯齿功能
vTextPaint.setTextSize(dipToPx(15));
vTextPaint.setColor(getResources().getColor(R.color.little_gray));
Typeface font= Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font);//字体风格
Rect bounds_Number=new Rect();
vTextPaint.getTextBounds(stepNumber,0,stepNumber.length(),bounds_Number);
canvas.drawText(stepNumber,centerX-bounds_Number.left,getHeight() / 2 + bounds_Number.height()/2-bounds_Number.top, vTextPaint);
}
/**
* 4.圆环中心[步数]的文字
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(15));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//抗锯齿功能
vTextPaint.setColor(getResources().getColor(R.color.little_gray));
String stepString = "步数";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX-bounds.left, getHeight() / 2 - bounds.height()-bounds.top, vTextPaint);
}

/**
* 获取当前步数的数字的高度
* @param fontSize 字体大小
* @return 字体高度
*/
public int getFontHeight(float fontSize) {
Paint paint = new Paint();
paint.setTextSize(fontSize);
Rect bounds_Number = new Rect();
paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
return bounds_Number.height();
}


/**
* dip 转换成px
* @param dip
* @return
*/

private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}

/**
* 所走的步数进度
*
* @param totalStepNum 设置的步数
* @param currentCounts 所走步数
*/
public void setCurrentCount(int startAngleNum,int totalStepNum, int currentCounts) {
stepNumber = currentCounts + "";
setTextSize(currentCounts);
/**如果当前走的步数超过总步数则还是圆*/
if (currentCounts > totalStepNum) {
currentCounts = totalStepNum;
}
/**所走步数占用总共步数的百分比*/
float scale = (float) currentCounts / totalStepNum;
/**开始所走的步数占总步速的百分比*/
float start=(float)startAngleNum/totalStepNum;
float startAngleLength=start*angleLength;
/**换算成弧度最后要到达的角度的长度-->弧长*/
float currentAngleLength = scale * angleLength;
/**开始执行动画*/
setAnimation(startAngleLength, currentAngleLength, animationLength);
}

/**
* 为进度设置动画
* ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
* 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
* 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
* 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
* 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
*
* @param last
* @param current
*/
private void setAnimation(float last, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last,current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}



/**
* 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
if (length <= 4) {
numberTextSize = dipToPx(15);
} else if (length > 4 && length <= 6) {
numberTextSize = dipToPx(12);
} else if (length > 6 && length <= 8) {
numberTextSize = dipToPx(10);
} else if (length > 8) {
numberTextSize = dipToPx(5);
}
}
}

颜色什么的各位自行配色吧,喜欢配啥配啥

二。用法

1
2
3
4
5
6
7
8
9
RunningView runingView;
/**
* 所走的步数进度
*
* @param startAngleNum开始的步数
* @param totalStepNum 最大的步数
* @param currentCounts 当前的步数
*/
runningView.setCurrentCount(0, 10000, 1781);

好的,自定义 view 第一弹到此结束,第二弹左下体重 View 敬请期待

更新于

请我喝[茶]~( ̄▽ ̄)~*

小翘 微信支付

微信支付

小翘 支付宝

支付宝

小翘 贝宝

贝宝