Skip to content

Commit 26b3ef5

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents ff3d9c1 + 8ed1e3b commit 26b3ef5

1 file changed

Lines changed: 350 additions & 0 deletions

File tree

README.md

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
# RandomTextView
2+
滚动显示TextView的数字,支持自定义每个字符速度。
3+
4+
感觉可以一用,一定要顺手star我哦~
5+
6+
最近在掘金这个干货平台上发了几篇博文,[这里进入我的掘金主页](http://gold.xitu.io/user/580f1397da2f60004f422d18)
7+
8+
看掘金APP中文章数据的数字滚动起来很动感,效果很棒,
9+
10+
于是决定把它通过自定义View编写出来,方便自己和大家调用。
11+
12+
本文Github代码链接
13+
https://github.com/AndroidMsky/RandomTextView
14+
15+
作者博客地址:
16+
http://blog.csdn.net/androidmsky?viewmode=list
17+
18+
先看看掘金的效果:
19+
20+
![这里写图片描述](http://img.blog.csdn.net/20161102161400896)
21+
22+
23+
我们自己实现的效果:
24+
25+
![这里写图片描述](http://img.blog.csdn.net/20161103091026709)
26+
27+
28+
29+
感觉可以一用,一定要star我哦~
30+
31+
32+
接下来介绍一下我的自定义View RandomTextView的用法和原理
33+
34+
用法
35+
--
36+
考入
37+
38+
[RandomTextView.java](https://github.com/AndroidMsky/RandomTextView/tree/master/app/src/main/java/com/example/liangmutian/randomtextview/view)
39+
40+
只有200行绝对轻量方便。
41+
42+
xml中定义:
43+
44+
```
45+
<com.example.liangmutian.randomtextview.view.RandomTextView
46+
android:id="@+id/rtv"
47+
android:layout_width="wrap_content"
48+
android:layout_height="wrap_content"
49+
android:layout_centerHorizontal="true"
50+
android:layout_centerVertical="true"
51+
android:padding="0px"
52+
android:text="123456"
53+
android:textSize="28sp"/>
54+
```
55+
56+
很开心的事,RandomTextView继承自TextView所以可以使用TextView的所有方法。color,size等等直接去定义就OK啦。
57+
58+
所有位数相同速度滚动:
59+
```
60+
mRandomTextView.setText("876543");
61+
mRandomTextView.setPianyilian(RandomTextView.ALL);
62+
mRandomTextView.start();
63+
```
64+
从左到右侧由快到慢滚动:
65+
66+
```
67+
mRandomTextView.setText("12313288");
68+
mRandomTextView.setPianyilian(RandomTextView.FIRSTF_FIRST);
69+
mRandomTextView.start();
70+
71+
```
72+
从左到右侧由慢到快滚动:
73+
74+
```
75+
mRandomTextView.setText("9078111123");
76+
mRandomTextView.setPianyilian(RandomTextView.FIRSTF_LAST);
77+
mRandomTextView.start();
78+
```
79+
自定义每位数字的速度滚动(每帧滚动的像素):
80+
81+
```
82+
mRandomTextView.setText("909878");
83+
pianyiliang[0] = 7;
84+
pianyiliang[1] = 6;
85+
pianyiliang[2] = 12;
86+
pianyiliang[3] = 8;
87+
pianyiliang[4] = 18;
88+
pianyiliang[5] = 10;
89+
mRandomTextView.setPianyilian(pianyiliang);
90+
mRandomTextView.start();
91+
```
92+
自定义滚动行数(默认10行):
93+
94+
```
95+
mRandomTextView.setMaxLine(20);
96+
```
97+
98+
原理
99+
--
100+
用TextView去绘制10(maxLine可设置)行文字,调用canvas.drawText去绘制出来,在绘制的Y坐标不断增加便宜量,去改变绘制的高度,通过handler.postDelayed(this, 20);不断增加便宜量,并且不断判断所有位数字最后一行绘制完毕的时候,结束handler的循环调用。
101+
102+
需要的变量:
103+
104+
```
105+
//高位快
106+
public static final int FIRSTF_FIRST = 0;
107+
//高位慢
108+
public static final int FIRSTF_LAST = 1;
109+
//速度相同
110+
public static final int ALL = 2;
111+
//用户自定义速度
112+
public static final int USER = 3;
113+
//偏移速度类型
114+
private int pianyiliangTpye;
115+
116+
// 滚动总行数 可设置
117+
private int maxLine = 10;
118+
// 当前字符串长度
119+
private int numLength = 0;
120+
// 当前text
121+
private String text;
122+
123+
124+
//滚动速度数组
125+
private int[] pianyilianglist;
126+
//总滚动距离数组
127+
private int[] pianyiliangSum;
128+
//滚动完成判断
129+
private int[] overLine;
130+
131+
private Paint p;
132+
//第一次绘制
133+
private boolean firstIn = true;
134+
//滚动中
135+
private boolean auto = true;
136+
137+
//text int值列表
138+
private ArrayList<Integer> arrayListText;
139+
140+
//字体宽度
141+
private float f0;
142+
143+
//基准线
144+
private int baseline;
145+
```
146+
147+
OnDraw方法:
148+
149+
```
150+
@Override
151+
protected void onDraw(Canvas canvas) {
152+
153+
if (firstIn) {
154+
firstIn = false;
155+
super.onDraw(canvas);
156+
p = getPaint();
157+
Paint.FontMetricsInt fontMetrics = p.getFontMetricsInt();
158+
baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
159+
float[] widths = new float[4];
160+
p.getTextWidths("9999", widths);
161+
f0 = widths[0];
162+
invalidate();
163+
}
164+
drawNumber(canvas);
165+
166+
```
167+
自一次进入onDraw方法时,做了如下几件事情:
168+
**1.**去获取当前正确的画笔p = getPaint();从而保证xml中配置的大小颜色等有效。
169+
**2.**通过当前画笔去计算正确的drawText基准线。
170+
baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
171+
**3.**等到数字的宽度。方便横向绘制。
172+
p.getTextWidths("9999", widths);f0 = widths[0];
173+
**4.**直接通知view重绘。
174+
invalidate();
175+
176+
我们自己的绘制drawNumber方法:
177+
178+
```
179+
private void drawNumber(Canvas canvas) {
180+
181+
for (int j = 0; j < numLength; j++) {
182+
183+
for (int i = 1; i < maxLine; i++) {
184+
185+
186+
if (i == maxLine - 1 && i * baseline + pianyiliangSum[j] <= baseline)
187+
188+
{
189+
pianyilianglist[j] = 0;
190+
overLine[j] = 1;
191+
int auto = 0;
192+
for (int k = 0; k < numLength; k++) {
193+
auto += overLine[k];
194+
}
195+
if (auto == numLength * 2 - 1) {
196+
this.auto = false;
197+
handler.removeCallbacks(task);
198+
invalidate();
199+
}
200+
201+
}
202+
if (overLine[j] == 0)
203+
204+
canvas.drawText(setBack(arrayListText.get(j), maxLine - i - 1) + "", 0 + f0 * j,
205+
i * baseline + pianyiliangSum[j], p);
206+
207+
else {
208+
//定位后画一次就好啦
209+
if (overLine[j] == 1) {
210+
overLine[j]++;
211+
canvas.drawText(arrayListText.get(j) + "", 0 + f0 * j,
212+
baseline, p);
213+
}
214+
215+
//break;
216+
}}
217+
}}
218+
```
219+
这里逻辑想对复杂时间复杂度达到了O(绘制行数*字符串位数),是个双重循环的绘制。
220+
第一层我们称之为J循环,J循环每次循环的内容是绘制一列。
221+
第二层循环称之为I循环,I循环负责绘制每行的每一个字符。
222+
223+
每次进入I循环的第一件事情是检查当前字符位,是不是最后一个
224+
225+
```
226+
if (i == maxLine - 1 && i * baseline + pianyiliangSum[j] <= baseline)
227+
```
228+
如果是,则归零便宜量,修改标志位
229+
```
230+
pianyilianglist[j] = 0;
231+
overLine[j] = 1;
232+
```
233+
之后去判段所有字符位是否全部绘制到最后一个:
234+
235+
```
236+
int auto = 0;
237+
for (int k = 0; k < numLength; k++) {
238+
auto += overLine[k];}
239+
if (auto == numLength * 2 - 1) {
240+
this.auto = false;
241+
handler.removeCallbacks(task);
242+
invalidate();}
243+
```
244+
如果是则讲自动循环刷新的方法取消掉,并且通知view进行最后一次定位绘制。
245+
以上就是进入i循环先对是否绘制结束的判断。
246+
247+
如果没有结束那么继续绘制:
248+
249+
```
250+
if (overLine[j] == 0)
251+
252+
canvas.drawText(setBack(arrayListText.get(j), maxLine - i - 1) + "", 0 + f0 * j,i * baseline +pianyiliangSum[j], p);
253+
else {
254+
if (overLine[j] == 1) {
255+
//定位后画一次就好啦
256+
overLine[j]++;
257+
canvas.drawText(arrayListText.get(j) + "", 0 + f0 * j,
258+
baseline, p);
259+
}
260+
}
261+
```
262+
263+
overLine[j]中的值的意思为:0表示还没绘制到最后一行,1表示为绘制到最后一行没有进行最后的定位绘制,2表示已经进行了定位绘制。
264+
265+
可能对于初学者最难的就是drawText的坐标问题,x坐标比较简单
266+
就是字符的宽度并且随着循环去变化:
267+
```
268+
0 + f0 * j
269+
```
270+
Y坐标就是当前行的基准值+上当前便宜量:
271+
272+
```
273+
i * baseline + pianyiliangSum[j]
274+
```
275+
276+
每隔20毫秒去计算当前便宜量并通知刷新view:
277+
278+
```
279+
private final Runnable task = new Runnable() {
280+
281+
public void run() {
282+
// TODO Auto-generated method stub
283+
if (auto) {
284+
handler.postDelayed(this, 20);
285+
286+
for (int j = 0; j < numLength; j++) {
287+
pianyiliangSum[j] -= pianyilianglist[j];
288+
289+
}
290+
invalidate();
291+
}
292+
293+
}
294+
};
295+
```
296+
帮助计算9上面的是几。8上面是几
297+
298+
```
299+
//设置上方数字0-9递减
300+
private int setBack(int c, int back) {
301+
302+
if (back == 0) return c;
303+
304+
back = back % 10;
305+
306+
int re = c - back;
307+
308+
if (re < 0) re = re + 10;
309+
310+
return re;
311+
}
312+
```
313+
314+
讲字符串转换为INT数组:
315+
316+
```
317+
private ArrayList<Integer> getList(String s) {
318+
319+
ArrayList<Integer> arrayList = new ArrayList<Integer>();
320+
321+
for (int i = 0; i < s.length(); i++) {
322+
323+
String ss = s.substring(i, i + 1);
324+
325+
int a = Integer.parseInt(ss);
326+
327+
arrayList.add(a);
328+
}
329+
return arrayList;
330+
331+
}
332+
```
333+
绘制原理的逻辑就讲完啦,RandomTextView可以投入使用啦,自定义view并不难,只要你知道安卓API能让你能干什么,你想干什么,你可能马上就知道你应该怎么做啦。
334+
335+
欢迎关注作者。欢迎评论讨论。欢迎拍砖。
336+
337+
如果觉得这篇文章对你有帮助 欢迎打赏,
338+
339+
欢迎star,Fork我的github。
340+
341+
喜欢作者的也可以Follow。也算对作者的一种支持。
342+
本文Github代码链接
343+
https://github.com/AndroidMsky/RandomTextView
344+
345+
欢迎加作者自营安卓开发交流群:308372687
346+
![这里写图片描述](http://img.blog.csdn.net/20161028111556438)
347+
348+
349+
350+
博主原创未经允许不许转载。

0 commit comments

Comments
 (0)