本文实例讲解的是如何画一个满满圆形水波纹loadingview,这类效果应用场景很多,比如内存占用百分比之类的,分享给大家供大家参考,具体内容如下
效果图如下:
预备的知识:
- 1.贝塞尔曲线 如果你不了解,可以来这里进行基础知识储备:神奇的贝塞尔曲线
- 2.paint.setxfermode() 以及porterduffxfermode
千万不要被这个b的名字吓到,不熟悉看到可能会认为很难记,其实 只要站在巨人的丁丁上 还是很简单的。
好了 废话不多说 ,跟我一步步来做一个炫酷的view吧。
首先给一些属性,在构造器里初始化(不要再ondraw new东西不要再ondraw new东西不要再ondraw new东西不要再ondraw new东西)
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
|
//绘制波纹 private paint mwavepaint; private porterduffxfermode mmode = new porterduffxfermode(porterduff.mode.xor); //设置mode 为xor //绘制圆 private paint mcirclepaint; private canvas mcanvas; //我们自己的画布 private bitmap mbitmap; private int mwidth; private int mheight; public waveloadingview(context context) { this (context, null ); } public waveloadingview(context context, attributeset attrs) { this (context, attrs, 0 ); } public waveloadingview(context context, attributeset attrs, int defstyleattr) { super (context, attrs, defstyleattr); mwavepaint = new paint(); mwavepaint.setcolor(color.parsecolor( "#33b5e5" )); mcirclepaint = new paint(); mcirclepaint.setcolor(color.parsecolor( "#99cc00" )); } @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; } if (heightmode == measurespec.exactly) { mheight = heightsize; } setmeasureddimension(mwidth, mheight); mbitmap = bitmap.createbitmap( 300 , 300 , bitmap.config.argb_8888); //生成一个bitmap mcanvas = new canvas(mbitmap); //讲bitmp放在我们自己的画布上,实际上mcanvas.draw的时候 改变的是这个bitmap对象 } |
然后,我们给他绘制一点东西,用来介绍porterduffxfermode
1
2
3
4
5
6
7
8
|
@override protected void ondraw(canvas canvas) { mcanvas.drawcircle( 100 , 100 , 50 ,mcirclepaint); mcanvas.drawrect( 100 , 100 , 200 , 200 ,mwavepaint); canvas.drawbitmap(mbitmap, 0 , 0 , null ); super .ondraw(canvas); } |
嗯,可以看到 是我们现在自己的画布上铺了一个bitmap(这里可以理解canvas为桌子 bitmap为画纸,我们在bimap上画画), 然后在bitmap上画了 一个圆,和一个矩形。最后把我们的mbitmap画到系统的画布上(显示到屏幕上),得到了以下效果。
然后我们用setxfermode()方法给他设置一个mode,这里设置xor。
可以发现! 相交的地方消失了! 是不是很神奇。
在改一个mode 试试
1
|
private porterduffxfermode mmode = new porterduffxfermode(porterduff.mode.dst_over); |
可以看到 圆形跑到了矩形上面来。 然后巨人给我们总结各个模式如了下图,这里给一个说明dst为先画的 src为后画的:.
大家可以根据这个规律试一下。
现在,我们把圆和矩形重叠。模式去掉。
1
2
3
4
5
6
7
8
9
10
11
|
protected void ondraw(canvas canvas) { //dst mcanvas.drawcircle( 150 , 150 , 50 ,mcirclepaint); / mwavepaint.setxfermode(mmode); //src mcanvas.drawrect( 100 , 100 , 200 , 200 ,mwavepaint); canvas.drawbitmap(mbitmap, 0 , 0 , null ); super .ondraw(canvas); } |
我的圆怎么没了。。 其实圆是被覆盖掉了。 然后我们想实现一个效果,就是在圆的范围内,显示矩形的内容,该怎么做呢。自己照着图找找吧哈哈。
我们要实现的是一个圆形的水波纹那种loadingview。。首要就是实现这个水波纹。
这时候贝塞尔曲线就派上用场了。这里采用三阶贝塞尔, 不停地改变x 模拟水波效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
if (x > 50 ) { isleft = true ; } else if (x < 0 ) { isleft = false ; } <span style= "white-space:pre" > </span> if (y > - 50 ) { //大于-50是因为辅助点是50 为了让他充满整个屏幕 y--; } if (isleft) { x = x - 1 ; } else { x = x + 1 ; } mpath.reset(); mpath.moveto( 0 , y); mpath.cubicto( 100 + x* 2 , 50 + y, 100 + x* 2 , y- 50 , mwidth, y); //前两个参数是辅助点 mpath.lineto(mwidth, mheight); //充满整个画布 mpath.lineto( 0 , mheight); //充满整个画布 mpath.close(); |
之后用mcanvas来绘制这个bitmap,要注意的是 绘制之前要清空mbitmap,不然path会重叠
1
2
3
4
5
6
7
8
9
|
mbitmap.erasecolor(color.parsecolor( "#00000000" )); //dst mcanvas.drawpath(mpath, mpaint); canvas.drawbitmap(mbitmap, 0 , 0 , null ); postinvalidatedelayed( 10 ); |
哈,水波效果出来了。 接着想办法让他画到一个圆形中。 首先绘制一个圆
1
|
mcanvas.drawcircle(mwidth / 2 , mheight / 2 , mwidth / 2 , msrcpaint); |
现在让我们回到刚才的问题,如何在dst的范围内绘制src呢。。。答案是。。src_in 你找对了吗。添加模式
1
2
3
4
5
6
7
8
|
//dst mcanvas.drawcircle(mwidth / 2 , mheight / 2 , mwidth / 2 , msrcpaint); mpaint.setxfermode(mmode); //src mcanvas.drawpath(mpath, mpaint); canvas.drawbitmap(mbitmap, 0 , 0 , null ); |
是不是有点感觉了。如果不这样做 就需要考虑好多问题。动态计算沿着圆弧x,y坐标 计算arcto的范围。
完善一下,添加一个percent来代表进度,让y来根据percent动态改变
y = (int) ((1-mpercent /100f) *mheight);
添加setpercent方法
1
2
3
|
public void setpercent( int percent){ mpercent = percent; } |
画上百分比的文字。
1
2
3
|
string str = mpercent + "%" ; float txtlength = mtextpaint.measuretext(str); canvas.drawtext(mpercent + "%" , mwidth / 2 -txtlength/ 2 , mheight / 2 , mtextpaint); |
然后配合seekbar,最后改改字体大小 画笔透明度。 添加个背景图 就成了效果图上的效果。
是不是很有趣,大家可以动手实现一下!