本文實例講解的是如何畫一個滿滿圓形水波紋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,最后改改字體大小 畫筆透明度。 添加個背景圖 就成了效果圖上的效果。
是不是很有趣,大家可以動手實現一下!