欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义View实现支付宝咻一咻效果

程序员文章站 2023-12-17 18:10:16
本篇文章介绍自定义view配合属性动画来实现如下的效果 实现思路挺简单: 画一个半透明的圆 实现两种动画效果,点击时扩散和不点击时扩散回收 使用...

本篇文章介绍自定义view配合属性动画来实现如下的效果

Android自定义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来说更加流畅,该方式大部分参考别人博客上的代码来实现,但是如果仅仅只是就拿来用不总结是不会成为自己的知识的,因此有了这篇博客。

参考:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: