Android波纹扩散效果之仿支付宝咻一咻功能实现波纹扩散特效
今年春节晚会没看尽兴,被支付宝集福给添了一段插曲,朋友们都在那数定时间段不停的咻一咻,哇,我咻到一个敬业福,不可能的,哈哈。那么咻一咻功能基于程序代码是怎么实现的呢?下面小编给大家分享本教程帮助大家学习android波纹扩散效果之仿支付宝咻一咻功能实现波纹扩散特效,具体内容如下所示:
先来看看这个效果
这是我的在only上添加的效果,说实话,only现在都还只是半成品,台面都上不了,怪自己技术不行,也太懒了
ps:这个view也是我模仿了人家的效果,参考了人家的思路写的,不是纯手撸,罪过罪过,网上应该也能找到很多这样的效果,我只是加入了一些自己的需求在里面
我么新建一个工程——whew
roundimageview
这个之前讲过网上 的粒子,把头像变成圆形的,这里就不多说了,直接撸代码吧!
package com.lgl.whew; /** * 圆形头像 * created by lgl on 2016/1/12. */ import android.content.context; import android.content.res.typedarray; import android.graphics.bitmap; import android.graphics.canvas; import android.graphics.paint; import android.graphics.porterduff; import android.graphics.porterduffxfermode; import android.graphics.rect; import android.graphics.drawable.bitmapdrawable; import android.graphics.drawable.drawable; import android.graphics.drawable.ninepatchdrawable; import android.util.attributeset; import android.widget.imageview; /** * 圆形imageview,可设置最多两个宽度不同且颜色不同的圆形边框。 * * 设置颜色在xml布局文件中由自定义属性配置参数指定 */ public class roundimageview extends imageview { private int mborderthickness = 0; private context mcontext; private int defaultcolor = 0xffffffff; // 如果只有其中一个有值,则只画一个圆形边框 private int mborderoutsidecolor = 0; private int mborderinsidecolor = 0; // 控件默认长、宽 private int defaultwidth = 0; private int defaultheight = 0; public roundimageview(context context) { super(context); mcontext = context; } public roundimageview(context context, attributeset attrs) { super(context, attrs); mcontext = context; setcustomattributes(attrs); } public roundimageview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); mcontext = context; setcustomattributes(attrs); } private void setcustomattributes(attributeset attrs) { typedarray a = mcontext.obtainstyledattributes(attrs, r.styleable.roundedimageview); mborderthickness = a.getdimensionpixelsize( r.styleable.roundedimageview_border_thickness, 0); mborderoutsidecolor = a .getcolor(r.styleable.roundedimageview_border_outside_color, defaultcolor); mborderinsidecolor = a.getcolor( r.styleable.roundedimageview_border_inside_color, defaultcolor); } @override protected void ondraw(canvas canvas) { drawable drawable = getdrawable(); if (drawable == null) { return; } if (getwidth() == 0 || getheight() == 0) { return; } this.measure(0, 0); if (drawable.getclass() == ninepatchdrawable.class) return; bitmap b = ((bitmapdrawable) drawable).getbitmap(); bitmap bitmap = b.copy(bitmap.config.argb_8888, true); if (defaultwidth == 0) { defaultwidth = getwidth(); } if (defaultheight == 0) { defaultheight = getheight(); } int radius = 0; if (mborderinsidecolor != defaultcolor && mborderoutsidecolor != defaultcolor) {// 定义画两个边框,分别为外圆边框和内圆边框 radius = (defaultwidth < defaultheight ? defaultwidth : defaultheight) / 2 - 2 * mborderthickness; // 画内圆 drawcircleborder(canvas, radius + mborderthickness / 2, mborderinsidecolor); // 画外圆 drawcircleborder(canvas, radius + mborderthickness + mborderthickness / 2, mborderoutsidecolor); } else if (mborderinsidecolor != defaultcolor && mborderoutsidecolor == defaultcolor) {// 定义画一个边框 radius = (defaultwidth < defaultheight ? defaultwidth : defaultheight) / 2 - mborderthickness; drawcircleborder(canvas, radius + mborderthickness / 2, mborderinsidecolor); } else if (mborderinsidecolor == defaultcolor && mborderoutsidecolor != defaultcolor) {// 定义画一个边框 radius = (defaultwidth < defaultheight ? defaultwidth : defaultheight) / 2 - mborderthickness; drawcircleborder(canvas, radius + mborderthickness / 2, mborderoutsidecolor); } else {// 没有边框 radius = (defaultwidth < defaultheight ? defaultwidth : defaultheight) / 2; } bitmap roundbitmap = getcroppedroundbitmap(bitmap, radius); canvas.drawbitmap(roundbitmap, defaultwidth / 2 - radius, defaultheight / 2 - radius, null); } /** * 获取裁剪后的圆形图片 */ public bitmap getcroppedroundbitmap(bitmap bmp, int radius) { bitmap scaledsrcbmp; int diameter = radius * 2; // 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片 int bmpwidth = bmp.getwidth(); int bmpheight = bmp.getheight(); int squarewidth = 0, squareheight = 0; int x = 0, y = 0; bitmap squarebitmap; if (bmpheight > bmpwidth) {// 高大于宽 squarewidth = squareheight = bmpwidth; x = 0; y = (bmpheight - bmpwidth) / 2; // 截取正方形图片 squarebitmap = bitmap.createbitmap(bmp, x, y, squarewidth, squareheight); } else if (bmpheight < bmpwidth) {// 宽大于高 squarewidth = squareheight = bmpheight; x = (bmpwidth - bmpheight) / 2; y = 0; squarebitmap = bitmap.createbitmap(bmp, x, y, squarewidth, squareheight); } else { squarebitmap = bmp; } if (squarebitmap.getwidth() != diameter || squarebitmap.getheight() != diameter) { scaledsrcbmp = bitmap.createscaledbitmap(squarebitmap, diameter, diameter, true); } else { scaledsrcbmp = squarebitmap; } bitmap output = bitmap.createbitmap(scaledsrcbmp.getwidth(), scaledsrcbmp.getheight(), bitmap.config.argb_8888); canvas canvas = new canvas(output); paint paint = new paint(); rect rect = new rect(0, 0, scaledsrcbmp.getwidth(), scaledsrcbmp.getheight()); paint.setantialias(true); paint.setfilterbitmap(true); paint.setdither(true); canvas.drawargb(0, 0, 0, 0); canvas.drawcircle(scaledsrcbmp.getwidth() / 2, scaledsrcbmp.getheight() / 2, scaledsrcbmp.getwidth() / 2, paint); paint.setxfermode(new porterduffxfermode(porterduff.mode.src_in)); canvas.drawbitmap(scaledsrcbmp, rect, rect, paint); bmp = null; squarebitmap = null; scaledsrcbmp = null; return output; } /** * 边缘画圆 */ private void drawcircleborder(canvas canvas, int radius, int color) { paint paint = new paint(); /* 去锯齿 */ paint.setantialias(true); paint.setfilterbitmap(true); paint.setdither(true); paint.setcolor(color); /* 设置paint的 style 为stroke:空心 */ paint.setstyle(paint.style.stroke); /* 设置paint的外框宽度 */ paint.setstrokewidth(mborderthickness); canvas.drawcircle(defaultwidth / 2, defaultheight / 2, radius, paint); } }
这里值得注意的是,要使用这个必须自定义一些属性,我们在values下新建一个attr.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="roundedimageview"> <attr name="border_thickness" format="dimension" /> <attr name="border_inside_color" format="color" /> <attr name="border_outside_color" format="color"></attr> </declare-styleable> </resources>
然后在xml文件中引入命名空间
xmlns:imagecontrol=http://schemas.android.com/apk/res-auto
我们直接看layout_mian.xml吧
layout_mian.xml
就一些布局咯
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:imagecontrol="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <relativelayout android:layout_width="match_parent" android:layout_height="wrap_content" > <com.lgl.whew.whewview android:id="@+id/wv" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.lgl.whew.roundimageview android:id="@+id/my_photo" android:layout_width="100dp" android:layout_height="100dp" android:layout_centerinparent="true" android:src="@drawable/myphoto" imagecontrol:border_inside_color="#bc0978" imagecontrol:border_outside_color="#ba3456" imagecontrol:border_thickness="1dp" /> </relativelayout> </linearlayout>
这样你就可以使用圆形图片了,我们接下来看波纹的绘制
whewview
package com.lgl.whew; import java.util.arraylist; import java.util.list; import android.content.context; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.util.attributeset; import android.view.view; /** * 模仿咻一咻 * * @author lgl * */ public class whewview extends view { private paint paint; private int maxwidth = 255; // 是否运行 private boolean isstarting = false; private list<string> alphalist = new arraylist<string>(); private list<string> startwidthlist = new arraylist<string>(); public whewview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); // todo auto-generated constructor stub init(); } public whewview(context context, attributeset attrs) { super(context, attrs); // todo auto-generated constructor stub init(); } public whewview(context context) { super(context); // todo auto-generated constructor stub init(); } private void init() { paint = new paint(); // 设置博文的颜色 paint.setcolor(0x0059ccf5); alphalist.add("255");// 圆心的不透明度 startwidthlist.add("0"); } @override public void ondraw(canvas canvas) { super.ondraw(canvas); setbackgroundcolor(color.transparent);// 颜色:完全透明 // 依次绘制 同心圆 for (int i = 0; i < alphalist.size(); i++) { int alpha = integer.parseint(alphalist.get(i)); // 圆半径 int startwidth = integer.parseint(startwidthlist.get(i)); paint.setalpha(alpha); // 这个半径决定你想要多大的扩散面积 canvas.drawcircle(getwidth() / 2, getheight() / 2, startwidth + 50, paint); // 同心圆扩散 if (isstarting && alpha > 0 && startwidth < maxwidth) { alphalist.set(i, (alpha - 1) + ""); startwidthlist.set(i, (startwidth + 1) + ""); } } if (isstarting && integer .parseint(startwidthlist.get(startwidthlist.size() - 1)) == maxwidth / 5) { alphalist.add("255"); startwidthlist.add("0"); } // 同心圆数量达到10个,删除最外层圆 if (isstarting && startwidthlist.size() == 10) { startwidthlist.remove(0); alphalist.remove(0); } // 刷新界面 invalidate(); } // 执行动画 public void start() { isstarting = true; } // 停止动画 public void stop() { isstarting = false; } // 判断是都在不在执行 public boolean isstarting() { return isstarting; } }
这里我们看到,对外有几个方法,一个开始动画,一个停止动画,一个检测是否正在运行
mainactivity
这里就是我们的需求了,我反编译了一下支付宝的apk,并没有找到他的咻一咻的音效,就在他的raw目录下随便找了一个,我们现在是需要这样一个需求
点击图片执行动画,并且每隔五分钟响一次
再次点击图片,停止动画,停止音效
我们先新建一个raw文件夹把音效拷贝进去吧
package com.lgl.whew; import android.app.activity; import android.media.audiomanager; import android.media.soundpool; import android.os.bundle; import android.os.handler; import android.view.view; import android.view.view.onclicklistener; public class mainactivity extends activity { private whewview wv; private roundimageview my_photo; private static final int nou = 1; // 声明一个soundpool private soundpool sp; // 定义一个整型用load();来设置suondidf private int music; private handler handler = new handler() { public void handlemessage(android.os.message msg) { if (msg.what == nou) { // 每隔10s响一次 handler.sendemptymessagedelayed(nou, 5000); sp.play(music, 1, 1, 0, 0, 1); } } }; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); } private void initview() { // 第一个参数为同时播放数据流的最大个数,第二数据流类型,第三为声音质量 sp = new soundpool(10, audiomanager.stream_system, 5); // 把你的声音素材放到res/raw里,第2个参数即为资源文件,第3个为音乐的优先级 music = sp.load(this, r.raw.hongbao_gq, 1); wv = (whewview) findviewbyid(r.id.wv); my_photo = (roundimageview) findviewbyid(r.id.my_photo); my_photo.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { if(wv.isstarting()){ //如果动画正在运行就停止,否则就继续执行 wv.stop(); //结束进程 handler.removemessages(nou); }else{ // 执行动画 wv.start(); handler.sendemptymessage(nou); } } }); } }
相信这里的逻辑不是很难吧,对了,我们在结束activity的时候也是要销毁这个进程的,不然…你懂的
@override protected void ondestroy() { // todo auto-generated method stub super.ondestroy(); handler.removemessages(nou); }
我们运行一下,想听效果的可以下载demo运行一下,我们这里做一个简单的演示
上一篇: java线程并发semaphore类示例