对于之前最火的无外乎集五福了,而五福除了加十个好友获得外,最直接的途径就是支付宝的咻一咻了。那么咻一咻具体有哪些实现方式呢?下面我们将一一介绍这几种思路的实现过程。
1.自定义view实现咻一咻
那么这种实现方法需要掌握canvas以及paint几乎所有的方法。其对程序员的专业知识要求极高。
用该种方式实现的优点有:
- ㈠这种是最复杂的实现方法,但其兼容性最高,其支持android的所有设备。
- ㈡其对内存要求不大,几乎不占用任何内存。
下面我们来看看是怎样实现其效果的:
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
|
public class xiuyixiuview extends view { /*** * 中心图片画笔 */ private paint paint; /*** * 水波圆圈画笔 */ private paint circlepaint; /*** * 用bitmap创建画布 */ private bitmap bitmap; /*** * 中心图片 */ private bitmap imagebit; /*** * 画布 */ private canvas canvas; /*** * 屏幕的宽 */ private int screenwidth; /*** * 屏幕的高 */ private int screenheight; /*** * 图片右上角坐标 */ private point pointlefttop; /*** * 图片右下角坐标 */ private point pointrightbottom; /*** * 记录圆圈 */ private list<lyjcircle> lyjcirclelist; /*** * 标记是否按下按钮,并且源泉是否扩散消失 */ private boolean isspread= false ; /*** * 默认没有按动时候的圆圈 */ private lyjcircle defaultcircle; public xiuyixiuview(context context, attributeset attrs) { super (context, attrs); this .lyjcirclelist= new arraylist<>(); screenwidth=lyjutils.getscreenwidth((activity) context); screenheight=lyjutils.getscreenheight((activity) context); bitmap = bitmap.createbitmap(screenwidth, screenheight, bitmap.config.argb_8888); // 设置位图的宽高 canvas = new canvas(); canvas.setbitmap(bitmap); paint= new paint(paint.dither_flag); paint.setantialias( true ); circlepaint= new paint(paint.dither_flag); circlepaint.setantialias( true ); imagebit= bitmapfactory.decoderesource(getresources(), r.drawable.bwa_homepage_yuyin); pointlefttop= new point((screenwidth/ 2 )-(imagebit.getwidth()/ 2 ),(screenheight/ 2 )-(imagebit.getheight()/ 2 )); pointrightbottom= new point(pointlefttop.x+imagebit.getwidth(),pointlefttop.y+imagebit.getheight()); canvas.drawbitmap(imagebit,pointlefttop.x,pointlefttop.y,paint); //取图片上的颜色 palette.generateasync(imagebit, new palette.paletteasynclistener() { @override public void ongenerated(palette palette) { palette.swatch swatch1 = palette.getvibrantswatch(); //充满活力的色板 circlepaint.setcolor(swatch1.getrgb()); circlepaint.setstyle(paint.style.stroke); circlepaint.setstrokewidth( 10 ); circlepaint.setalpha( 100 ); paint.setshadowlayer( 15 , 0 , 0 , swatch1.getrgb()); //设置阴影效果 int [] mcolors = new int [] { //渲染颜色 color.transparent,swatch1.getrgb() }; //范围,这里可以微调,实现你想要的渐变 float [] mpositions = new float [] { 0f, 0 .1f }; shader shader= new radialgradient(screenwidth / 2 ,screenheight / 2 ,imagebit.getwidth() / 2 + 10 ,mcolors, mpositions, shader.tilemode.mirror); circlepaint.setshader(shader); defaultcircle= new lyjcircle(screenwidth / 2 , screenheight / 2 , imagebit.getwidth() / 2 + 10 ); clearscreenanddrawlist(); message message = handler.obtainmessage( 1 ); handler.sendmessagedelayed(message, 1000 ); //发送message } }); } @override public boolean ontouchevent(motionevent event) { switch (event.getaction()){ case motionevent.action_down: break ; case motionevent.action_move: break ; case motionevent.action_up: isspread= true ; //是否按下图片 lyjcirclelist.add( new lyjcircle(screenwidth / 2 , screenheight / 2 , imagebit.getwidth() / 2 + 10 )); clearscreenanddrawlist(); invalidate(); break ; default : break ; } return true ; } private handler handler = new handler(){ public void handlemessage(message msg){ switch (msg.what) { case 1 : //定时更新界面 clearscreenanddrawlist(); invalidate(); message message = handler.obtainmessage( 1 ); handler.sendmessagedelayed(message, 200 ); } super .handlemessage(msg); } }; /** * 清掉屏幕上所有的圆圈,然后画出集合里面的圆圈 */ private void clearscreenanddrawlist() { canvas.drawcolor(color.transparent, porterduff.mode.clear); //判断是否按下图片,并且外圈执行完成没有。 if (!isspread){ circlepaint.setmaskfilter( null ); canvas.drawcircle(defaultcircle.getroundx(), defaultcircle.getroundy(),defaultcircle.getradiuloop(), circlepaint); // 画线 } else { for (lyjcircle lyjcircle : lyjcirclelist) { if (lyjcircle.getspreadradiu()== 0 ){ } else if (lyjcircle.getspreadradiu()>(lyjcircle.getradiu()+ 99 )){ //如果圆圈扩散半径大于图片半径+99,那么设置边缘模糊,也就是淡出的效果 circlepaint.setmaskfilter( new blurmaskfilter( 5 , blurmaskfilter.blur.outer)); canvas.drawcircle(lyjcircle.getroundx(), lyjcircle.getroundy(),lyjcircle.getspreadradiu(), circlepaint); // 画线 } else { //不是则按正常的环形渲染来 circlepaint.setmaskfilter( null ); canvas.drawcircle(lyjcircle.getroundx(), lyjcircle.getroundy(),lyjcircle.getspreadradiu(), circlepaint); // 画线 } } } canvas.drawbitmap(imagebit,pointlefttop.x,pointlefttop.y,paint); //释放小时了的圆圈 for ( int i= 0 ;i<lyjcirclelist.size();i++){ if (lyjcirclelist.get(i).getspreadradiu()== 0 ){ lyjcirclelist.remove(i); } } //如果没有点击图片发射出去的圆圈,那么就恢复默认缩放。 if (lyjcirclelist.size()<= 0 ){ isspread= false ; } } @override protected void ondraw(canvas canvas) { canvas.drawbitmap(bitmap, 0 , 0 , null ); } } |
圆类:
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
|
package com.example.liyuanjing.model; /** * created by liyuanjing on 2016/2/3. */ public class lyjcircle { private int roundx; //圆中心点x坐标 private int roundy; //圆中心点y坐标 private int radiu; //圆半径 private int currentradiu; //当前radiu private int lastradiu; //历史radiu private int spreadradiu; //加速半径 private int [] speed= new int []{ 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 }; //半径扩大速度。这里为匀速 private int speedlast= 0 ; //记录历史值 public lyjcircle( int roundx, int roundy, int radiu){ this .roundx=roundx; this .roundy=roundy; this .radiu=radiu; this .spreadradiu=radiu; this .currentradiu= this .radiu; this .lastradiu= this .currentradiu; } //获取半径 public int getradiu() { return radiu; } public void setradiu( int radiu) { this .radiu = radiu; } //获取加速半径 public int getspreadradiu(){ if (speedlast>=speed.length){ return 0 ; } spreadradiu+=speed[speedlast]; ++speedlast; return spreadradiu; } //获取循环缩放半径 public int getradiuloop() { if (currentradiu==lastradiu){ ++currentradiu; } else if (currentradiu>lastradiu){ if (currentradiu>(radiu+ 20 )){ currentradiu= 19 +radiu; lastradiu= 20 +radiu; } else { lastradiu=currentradiu; currentradiu+= 5 ; } } else { if (currentradiu<(radiu+ 9 )){ currentradiu= 10 +radiu; lastradiu= 9 +radiu; } else { lastradiu=currentradiu; currentradiu-= 5 ; } } return currentradiu; } public int getroundx() { return roundx; } public int getroundy() { return roundy; } } |
你可以修改如下两个地方,会产生视觉上真真的波纹效果:
①支付宝的背景图片是淡红色,衬托了红色的波纹。当然了你也可以将画布设置为透明淡红色。
②其为填充圆圈渲染,不是我的边框渲染效果,你可以将circlepaint.setstyle(paint.style.stroke);换成paint.style.fill.然后,微调shader的mpositions实现环形填充渐变。你也许会觉得,你看支付宝咻一咻圆圈弹开的时候内圈有波纹也像外弹开,其实那就是环形渐变,当你圆圈变大后,其渐变的范围也就变大了,自然你看到有颜色周围扩散的迹象。
2.属性动画实现咻一咻
其要掌握的只是基本只需要属性动画,在加一点线程方面有关的知识而已。
下面我们看看其实现步骤:
㈠自定义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
|
public class lyjcircleview extends view { private bitmap bitmap; private paint paint; private canvas canvas; private int screenwidth; private int screenheight; private boolean isspreadflag= false ; //标记是否发射完成 public boolean isspreadflag() { return isspreadflag; } public void setisspreadflag( boolean isspreadflag) { this .isspreadflag = isspreadflag; } public lyjcircleview(context context, int width, int height, int statusheight) { super (context); screenwidth= lyjutils.getscreenwidth((activity) context); screenheight=lyjutils.getscreenheight((activity) context); bitmap = bitmap.createbitmap(screenwidth, screenheight, bitmap.config.argb_8888); // 设置位图的宽高 canvas = new canvas(); canvas.setbitmap(bitmap); paint= new paint(paint.dither_flag); paint.setantialias( true ); paint.setcolor(color.red); paint.setstyle(paint.style.stroke); paint.setstrokewidth( 5 ); paint.setalpha( 100 ); paint.setshadowlayer( 10 , 0 , 0 , color.red); int [] mcolors = new int [] { color.transparent,color.red }; float [] mpositions = new float [] { 0f, 0 .1f }; shader shader= new radialgradient(screenwidth / 2 ,screenheight / 2 ,width / 2 + 10 ,mcolors, mpositions, shader.tilemode.mirror); paint.setshader(shader); canvas.drawcircle(screenwidth / 2 , (screenheight - statusheight) / 2 , width / 2 + 10 , paint); invalidate(); } @override protected void ondraw(canvas canvas) { canvas.drawbitmap(bitmap, 0 , 0 , null ); } } |
代码与上面差不多,就不注释了。
㈡实现activity即可
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
|
public class xiuyixiuactivity extends appcompatactivity { private imagebutton mimagebutton; private lyjcircleview lyjcircleview; private relativelayout relativelayout; private list<lyjcircleview> lyjcircleviewlist; private int statusbarheight; private animator anim; @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.xiuyixiu_activity_main); this .mimagebutton=(imagebutton)findviewbyid(r.id.xiuyixiu_imagebutton); this .relativelayout=(relativelayout)findviewbyid(r.id.xiuyixiu_relativelayout); this .lyjcircleviewlist= new arraylist<>(); this .mimagebutton.setonclicklistener( new view.onclicklistener() { @override public void onclick(view v) { lyjcircleview.setvisibility(view.gone); //发射圆圈,即将循环动画view隐藏 final lyjcircleview item= new lyjcircleview(xiuyixiuactivity. this , mimagebutton.getwidth(), mimagebutton.getheight(), statusbarheight); animator spreadanim = animatorinflater.loadanimator(xiuyixiuactivity. this , r.animator.circle_spread_animator); spreadanim.addlistener( new animator.animatorlistener() { @override public void onanimationstart(animator animation) { } @override public void onanimationend(animator animation) { item.setisspreadflag( true ); //动画执行完成,标记一下 } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); spreadanim.settarget(item); spreadanim.start(); lyjcircleviewlist.add(item); relativelayout.addview(item); relativelayout.invalidate(); message message = handler.obtainmessage( 1 ); handler.sendmessagedelayed(message, 10 ); //发送message,定时释放lyjcircleview } }); } private handler handler = new handler(){ public void handlemessage(message msg){ switch (msg.what) { case 1 : for ( int i= 0 ;i<lyjcircleviewlist.size();i++){ if (lyjcircleviewlist.get(i).isspreadflag()){ relativelayout.removeview(lyjcircleviewlist.get(i)); lyjcircleviewlist.remove(i); relativelayout.invalidate(); } } if (lyjcircleviewlist.size()<= 0 ){ lyjcircleview.setvisibility(view.visible); } message message = handler.obtainmessage( 1 ); handler.sendmessagedelayed(message, 10 ); } super .handlemessage(msg); } }; @override public void onwindowfocuschanged( boolean hasfocus) { super .onwindowfocuschanged(hasfocus); //获取状态栏高度 rect frame = new rect(); getwindow().getdecorview().getwindowvisibledisplayframe(frame); statusbarheight = frame.top; this .mimagebutton.post( new runnable() { @override public void run() { lyjcircleview = new lyjcircleview(xiuyixiuactivity. this , mimagebutton.getwidth(), mimagebutton.getheight(), statusbarheight); relativelayout.addview(lyjcircleview); relativelayout.postinvalidate(); // 加载动画 anim = animatorinflater.loadanimator(xiuyixiuactivity. this , r.animator.circle_scale_animator); anim.addlistener( new animator.animatorlistener() { @override public void onanimationstart(animator animation) { } @override public void onanimationend(animator animation) { anim.start(); //循环执行动画 } @override public void onanimationcancel(animator animation) { } @override public void onanimationrepeat(animator animation) { } }); anim.settarget(lyjcircleview); anim.start(); } }); } } |
㈢布局文件代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?xml version= "1.0" encoding= "utf-8" ?> <relativelayout xmlns:android= "http://schemas.android.com/apk/res/android" android:id= "@+id/xiuyixiu_relativelayout" android:layout_width= "match_parent" android:layout_height= "match_parent" > <imagebutton android:id= "@+id/xiuyixiu_imagebutton" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_centerinparent= "true" android:background= "@drawable/bwa_homepage_yuyin" /> </relativelayout> |
当然上面两个实现方法,我都只设置圆边框,没有填充,你可以设置为填充后,在微调渐变值。
其属性动画文件circle_scale_animator.xml:
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
|
<?xml version= "1.0" encoding= "utf-8" ?> <set xmlns:android= "http://schemas.android.com/apk/res/android" android:ordering= "together" > <objectanimator android:duration= "1000" android:propertyname= "scalex" android:valuefrom= "1.0" android:valueto= "1.2" android:valuetype= "floattype" > </objectanimator> <objectanimator android:duration= "1000" android:propertyname= "scaley" android:valuefrom= "1.0" android:valueto= "1.2" android:valuetype= "floattype" > </objectanimator> <objectanimator android:startoffset= "1000" android:duration= "1000" android:propertyname= "scalex" android:valuefrom= "1.2" android:valueto= "1.0" android:valuetype= "floattype" > </objectanimator> <objectanimator android:startoffset= "1000" android:duration= "1000" android:propertyname= "scaley" android:valuefrom= "1.2" android:valueto= "1.0" android:valuetype= "floattype" > </objectanimator> </set> |
另一个circle_spread_animator.xml为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?xml version= "1.0" encoding= "utf-8" ?> <set xmlns:android= "http://schemas.android.com/apk/res/android" > <objectanimator android:duration= "1000" android:propertyname= "scaley" android:valuefrom= "1.0" android:valueto= "2.0" android:valuetype= "floattype" > </objectanimator> <objectanimator android:duration= "1000" android:propertyname= "scalex" android:valuefrom= "1.0" android:valueto= "2.0" android:valuetype= "floattype" > </objectanimator> </set> |
以上就是本文的详细内容,希望对大家的学习有所帮助。