Android自定义View实现支付宝咻一咻效果
本篇文章介绍自定义view配合属性动画来实现如下的效果
实现思路挺简单:
- 画一个半透明的圆
- 实现两种动画效果,点击时扩散和不点击时扩散回收
- 使用线程的方式将上面两步结合起来
首先看下画半透明圆的部分
public class clickcircleview extends view { private bitmap bitmap; private paint paint; private canvas canvas; private boolean isspreadflag = false;//标记是否发射完成 public boolean isspreadflag() { return isspreadflag; } public void setisspreadflag(boolean isspreadflag) { this.isspreadflag = isspreadflag; } public clickcircleview(context context, int width, int height, int screenwidth, int screenheight) { super(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.white); paint.setstyle(paint.style.fill); paint.setalpha(50); canvas.drawcircle(screenwidth / 2, screenheight / 2, width / 2 + 10, paint); invalidate(); } @override protected void ondraw(canvas canvas) { canvas.drawbitmap(bitmap, 0, 0, null); } }
可以看到相关的属性都是设置在画笔上,然后直接调用画布的drawcircle()方法画出一个半透明的圆,最后调用invalidate()方法刷新view
一定要重写父类的ondraw()方法,否则自定义view不能生效
我们设置了一个标志位isspreadflag,作用是用来标记扩散动画是否完成
然后我们来实现两个动画效果
点击时扩散动画
<set xmlns:android="http://schemas.android.com/apk/res/android"> <objectanimator android:duration="1000" android:propertyname="scaley" android:valuefrom="1.0" android:valueto="1.8" android:valuetype="floattype" /> <objectanimator android:duration="1000" android:propertyname="scalex" android:valuefrom="1.0" android:valueto="1.8" android:valuetype="floattype" /> </set>
很简单,就是改变scale值,增大到1.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 android:duration="1000" android:propertyname="scaley" android:valuefrom="1.0" android:valueto="1.2" android:valuetype="floattype" /> <objectanimator android:duration="1000" android:propertyname="scalex" android:startoffset="1000" android:valuefrom="1.2" android:valueto="1.0" android:valuetype="floattype" /> <objectanimator android:duration="1000" android:propertyname="scaley" android:startoffset="1000" android:valuefrom="1.2" android:valueto="1.0" android:valuetype="floattype" /> </set>
和上个动画类似,startoffset参数可以用来控制animation的运行顺序,比如android:startoffset=”1000”表示设置该属性的动画延迟1秒执行
然后就是用线程来执行动画和逻辑的部分了
不点击时的动画部分
mxiuyixiubutton.post(new runnable() { @override public void run() { clickcircleview = new clickcircleview(customview1.this, mxiuyixiubutton.getwidth() , mxiuyixiubutton.getheight(), mxiuyixiulayout.getmeasuredwidth(), mxiuyixiulayout.getmeasuredheight()); clickcircleview.setvisibility(view.visible); mxiuyixiulayout.addview(clickcircleview); mxiuyixiulayout.postinvalidate(); // 加载动画 final animator anim = animatorinflater.loadanimator(customview1.this, r.animator.circle_scale_animator); anim.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { if (anim != null) { anim.start();//循环执行动画 } } }); anim.settarget(clickcircleview); anim.start(); } });
初始化好clickcircleview之后将这个view加入父布局中,然后加载动画并设置循环执行,最后使用postinvalidate()在子线程中刷新view
点击时的动画部分
mxiuyixiubutton.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { clickcircleview.setvisibility(view.gone);//发射圆圈,即将循环动画view隐藏 final clickcircleview item = new clickcircleview(customview1.this, mxiuyixiubutton.getwidth() , mxiuyixiubutton.getheight(), mxiuyixiulayout.getwidth(), mxiuyixiulayout.getheight()); animator spreadanim = animatorinflater.loadanimator(customview1.this, r.animator.circle_spread_animator); spreadanim.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { item.setisspreadflag(true);//动画执行完成,标记一下 } }); spreadanim.settarget(item); spreadanim.start(); clickcircleviewlist.add(item); mxiuyixiulayout.addview(item); mxiuyixiulayout.invalidate(); handler.post(circleviewrunnable); } });
隐藏不点击动画,初始化好clickcircleview后将该view加入list中并添加到父布局中,然后加载动画并在动画结束时添加isspreadflag标记,最后调用invalidate()方法刷新view并开启线程
线程部分
private runnable circleviewrunnable = new runnable() { public void run() { for (int i = 0; i < clickcircleviewlist.size(); i++) { if (clickcircleviewlist.get(i).isspreadflag()) { mxiuyixiulayout.removeview(clickcircleviewlist.get(i)); clickcircleviewlist.remove(i); mxiuyixiulayout.postinvalidate(); } } if (clickcircleviewlist.size() <= 0) { clickcircleview.setvisibility(view.visible); } handler.postdelayed(this, 100); } };
遍历list,将有isspreadflag标记的view从list和父布局中移除并刷新view,最后判断list如果为空的话将不点击时的动画显示出来
最后记得在ondestroy()里移除线程
@override protected void ondestroy() { super.ondestroy(); handler.removecallbacks(circleviewrunnable); }
使用自定义view配合属性动画来实现该效果耦合性较高,只是这种方式相比完全使用自定义view来说更加流畅,该方式大部分参考别人博客上的代码来实现,但是如果仅仅只是就拿来用不总结是不会成为自己的知识的,因此有了这篇博客。
参考:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。