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

Android实现红包雨动画效果

程序员文章站 2023-01-26 22:26:49
本文介绍了android实现红包雨动画效果,分享给大家,希望对大家有帮助 红包雨 关于实现上面红包雨效果步骤如下: 1.创建一个红包实体类 publ...

本文介绍了android实现红包雨动画效果,分享给大家,希望对大家有帮助

红包雨

Android实现红包雨动画效果

关于实现上面红包雨效果步骤如下:

1.创建一个红包实体类

public class redpacket {
  public float x, y;
  public float rotation;
  public float speed;
  public float rotationspeed;
  public int width, height;
  public bitmap bitmap;
  public int money;
  public boolean isrealred;

  public redpacket(context context, bitmap originalbitmap, int speed, float maxsize, float minsize, int viewwidth) {
    //获取一个显示红包大小的倍数
    double widthrandom = math.random();
    if (widthrandom < minsize || widthrandom > maxsize) {
      widthrandom = maxsize;
    }
    //红包的宽度
    width = (int) (originalbitmap.getwidth() * widthrandom);
    //红包的高度
    height = width * originalbitmap.getheight() / originalbitmap.getwidth();
    int mwidth = (viewwidth == 0) ? context.getresources().getdisplaymetrics().widthpixels : viewwidth;
    //生成红包bitmap
    bitmap = bitmap.createscaledbitmap(originalbitmap, width, height, true);
    originalbitmap.recycle();
    random random = new random();
    //红包起始位置x:[0,mwidth-width]
    int rx = random.nextint(mwidth) - width;
    x = rx <= 0 ? 0 : rx;
    //红包起始位置y
    y = -height;
    //初始化该红包的下落速度
    this.speed = speed + (float) math.random() * 1000;
    //初始化该红包的初始旋转角度
    rotation = (float) math.random() * 180 - 90;
    //初始化该红包的旋转速度
    rotationspeed = (float) math.random() * 90 - 45;
    //初始化是否为中奖红包
    isrealred = isrealredpacket();
  }

  /**
   * 判断当前点是否包含在区域内
   */
  public boolean iscontains(float x, float y) {
    //稍微扩大下点击的区域
    return this.x-50 < x && this.x +50 + width > x
        && this.y-50 < y && this.y+50 + height > y;
  }

  /**
   * 随机 是否为中奖红包
   */
  public boolean isrealredpacket() {
    random random = new random();
    int num = random.nextint(10) + 1;
    //如果[1,10]随机出的数字是2的倍数 为中奖红包
    if (num % 2 == 0) {
      money = num*2;//中奖金额
      return true;
    }
    return false;
  }

  /**
   * 回收图片
   */
  public void recycle() {
    if (bitmap!= null && !bitmap.isrecycled()){
      bitmap.recycle();
    }
  }
}

上面就红包实体类的源码,重点就是在创建红包实体的时候,初始化红包相关的值,如生成红包图片,图片的宽高,红包初始位置,下落速度等。比较简单。

2.自定义红包雨view

view初始化

 public redpackettest(context context, @nullable attributeset attrs) {
    super(context, attrs);
    final typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.redpacketstyle);
    //获取xml中配置的view的style属性,如下落红包数量,下落的基础速度,以及红包图片的最大最小范围
    count = typedarray.getint(r.styleable.redpacketstyle_count, 20);
    speed = typedarray.getint(r.styleable.redpacketstyle_speed, 20);
    minsize = typedarray.getfloat(r.styleable.redpacketstyle_min_size, 0.5f);
    maxsize = typedarray.getfloat(r.styleable.redpacketstyle_max_size, 1.2f);
    typedarray.recycle();
    init();
  }


  /**
   * 初始化
   */
  private void init() {
    //初始化画笔
    paint = new paint();
    paint.setfilterbitmap(true);
    paint.setdither(true);
    paint.setantialias(true);
    //创建一个属性动画,通过属性动画来控制刷新红包下落的位置
    animator = valueanimator.offloat(0, 1);
    //绘制view开启硬件加速
    setlayertype(view.layer_type_hardware, null);
   //初始化属性动画
    initanimator();
  }

  private void initanimator() {
    //每次动画更新的时候,更新红包下落的坐标值
    animator.addupdatelistener(new valueanimator.animatorupdatelistener() {
      @override
      public void onanimationupdate(valueanimator animation) {
        long nowtime = system.currenttimemillis();
        //获取两次动画更新之间的时间,以此来计算下落的高度
        float secs = (float) (nowtime - prevtime) / 1000f;
        prevtime = nowtime;
        for (int i = 0; i < redpacketlist.size(); ++i) {
          redpacket redpacket = redpacketlist.get(i);
          //更新红包的下落的位置y
          redpacket.y += (redpacket.speed * secs);

          //如果y坐标大于view的高度 说明划出屏幕,y重新设置起始位置,以及中奖属性
          if (redpacket.y > getheight()) {
            redpacket.y = 0 - redpacket.height;
            redpacket.isrealred = redpacket.isrealredpacket();
          }
          //更新红包的旋转的角度
          redpacket.rotation = redpacket.rotation
              + (redpacket.rotationspeed * secs);
        }
        //重绘
        invalidate();
      }
    });
    //属性动画无限循环
    animator.setrepeatcount(valueanimator.infinite);
    //属性值线性变换
    animator.setinterpolator(new linearinterpolator());
    animator.setduration(0);
  }

view绘制

 @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
    //获取自定义view的宽度
    mwidth = getmeasuredwidth();
  }

  @override
  protected void ondraw(final canvas canvas) {
    //遍历红包数组,绘制红包
    for (int i = 0; i < redpacketlist.size(); i++) {
      redpacket redpacket = redpacketlist.get(i);
      //将红包旋转redpacket.rotation角度后 移动到(redpacket.x,redpacket.y)进行绘制红包
      matrix m = new matrix();
      m.settranslate(-redpacket.width / 2, -redpacket.height / 2);
      m.postrotate(redpacket.rotation);
      m.posttranslate(redpacket.width / 2 + redpacket.x, redpacket.height / 2 + redpacket.y);
      //绘制红包
      canvas.drawbitmap(redpacket.bitmap, m, paint);
    }
  }

红包雨动画开始结束

 /**
   *停止动画
   */
  public void stoprainnow() {
    //清空红包数据
    clear();
    //重绘
    invalidate();
    //动画取消
    animator.cancel();
  }


  /**
   * 开始动画
   */
  public void startrain() {
    //清空红包数据
    clear();
    //添加红包
    setredpacketcount(count);
    prevtime = system.currenttimemillis();
    //动画开始
    animator.start();

  }

  public void setredpacketcount(int count) {
    if (mimgids == null || mimgids.length == 0)
      return;
    for (int i = 0; i < count; ++i) {
      //获取红包原始图片
      bitmap originalbitmap = bitmapfactory.decoderesource(getresources(), mimgids[0]);
      //生成红包实体类
      redpacket redpacket = new redpacket(getcontext(), originalbitmap, speed,maxsize,minsize,mwidth);
      //添加进入红包数组
      redpacketlist.add(redpacket);
    }
  }

  /**
   * 暂停红包雨
   */
  public void pauserain() {
    animator.cancel();
  }

  /**
   * 重新开始
   */
  public void restartrain() {
    animator.start();
  }

  /**
   * 清空红包数据,并回收红包中的bitmap
   */
  private void clear() {
    for (redpacket redpacket :redpacketlist) {
      redpacket.recycle();
    }
    redpacketlist.clear();
  }

红包点击事件

  @override
  public boolean ontouchevent(motionevent motionevent) {
    switch (motionevent.getaction()){
      case motionevent.action_down:
        //根据点击的坐标点,判断是否点击在红包的区域
        redpacket redpacket = isredpacketclick(motionevent.getx(), motionevent.gety());
        if (redpacket != null) {
          //如果点击在红包上,重新设置起始位置,以及中奖属性
          redpacket.y = 0 - redpacket.height;
          redpacket.isrealred = redpacket.isrealredpacket();
          if (onredpacketclicklistener != null) {
            onredpacketclicklistener.onredpacketclicklistener(redpacket);
          }
        }
        break;
      case motionevent.action_move:
        break;
      case motionevent.action_cancel:
      case motionevent.action_up:

        break;
    }
    return true;
  }

  //根据点击坐标点,遍历红包数组,看是否点击在红包上
  private redpacket isredpacketclick(float x, float y) {
    for (int i = redpacketlist.size() - 1; i >= 0; i --) {
      if (redpacketlist.get(i).iscontains(x, y)) {
        return redpacketlist.get(i);
      }
    }
    return null;
  }

关于自定义红包雨view的主要代码以及分析基本完成了。下面是自定义view的使用。

3.自定义view的使用

红包雨activity

public class redpacketactivity extends appcompatactivity implements view.onclicklistener {
  private redpackettest redrainview1;
  private button start, stop;
  private textview money;
  private int totalmoney = 0;
  alertdialog.builder ab;
  @override
  protected void oncreate(@nullable bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.red_rain);
    ab = new alertdialog.builder(redpacketactivity.this);
    start = (button) findviewbyid(r.id.start);
    stop = (button) findviewbyid(r.id.stop);
    money = (textview) findviewbyid(r.id.money);
    redrainview1 = (redpackettest) findviewbyid(r.id.red_packets_view1);
    start.setonclicklistener(this);
    stop.setonclicklistener(this);

  }

  @override
  public void onclick(view v) {
    if (v.getid() == r.id.start) {
      startredrain();
    } else if (v.getid() == r.id.stop) {
      stopredrain();
    }
  }

  /**
   * 开始下红包雨
   */
  private void startredrain() {
    redrainview1.startrain();
    redrainview1.setonredpacketclicklistener(new redpackettest.onredpacketclicklistener() {
      @override
      public void onredpacketclicklistener(redpacket redpacket) {
        redrainview1.pauserain();
        ab.setcancelable(false);
        ab.settitle("红包提醒");
        ab.setnegativebutton("继续抢红包", new dialoginterface.onclicklistener() {
          @override
          public void onclick(dialoginterface dialog, int which) {
            redrainview1.restartrain();
          }
        });

        if (redpacket.isrealred) {
          ab.setmessage("恭喜你,抢到了" + redpacket.money + "元!");
          totalmoney += redpacket.money;
          money.settext("中奖金额: " + totalmoney);
        } else {
          ab.setmessage("很遗憾,下次继续努力!");
        }
        redrainview1.post(new runnable() {
          @override
          public void run() {
            ab.show();
          }
        });
      }
    });
  }

  /**
   * 停止下红包雨
   */
  private void stopredrain() {
    totalmoney = 0;//金额清零
    redrainview1.stoprainnow();
  }

红包雨activity的xml

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#80000000">

  <imageview
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaletype="fitxy"
    android:src="@drawable/red_packets_bg" />
  <button
    android:id="@+id/start"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="开始"
    />
  <button
    android:id="@+id/stop"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignparentright="true"
    android:text="结束"
    />

  <textview
    android:id="@+id/money"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerhorizontal="true"
    android:text="中奖金额:"
    android:textsize="18sp"
    android:layout_margintop="10dp"
    />

  <com.example.test.redpacketrain.redpackettest
    android:id="@+id/red_packets_view1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:count="20"
    app:max_size="0.8"
    app:min_size="0.6"
    app:speed="500" />
</relativelayout>

自定义view的styleable

<resources>
  <declare-styleable name="redpacketstyle">
    <attr name="count" format="integer" />
    <attr name="speed" format="integer" />
    <attr name="max_size" format="float" />
    <attr name="min_size" format="float" />
  </declare-styleable>
</resources>

完整的自定义view代码

public class redpackettest extends view {
  private int[] mimgids = new int[]{
      r.drawable.red_packets_icon
  };//红包图片
  private int count;//红包数量
  private int speed;//下落速度
  private float maxsize;//红包大小的范围
  private float minsize;//红包大小的范围
  private int mwidth;//view宽度
  private valueanimator animator;//属性动画,用该动画来不断改变红包下落的坐标值

  private paint paint;//画笔
  private long prevtime;
  private arraylist<redpacket> redpacketlist = new arraylist<>();//红包数组

  public redpackettest(context context) {
    super(context);
    init();
  }

  public redpackettest(context context, @nullable attributeset attrs) {
    super(context, attrs);
    final typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.redpacketstyle);
    count = typedarray.getint(r.styleable.redpacketstyle_count, 20);
    speed = typedarray.getint(r.styleable.redpacketstyle_speed, 20);
    minsize = typedarray.getfloat(r.styleable.redpacketstyle_min_size, 0.5f);
    maxsize = typedarray.getfloat(r.styleable.redpacketstyle_max_size, 1.2f);
    typedarray.recycle();
    init();
  }


  /**
   * 初始化
   */
  private void init() {
    paint = new paint();
    paint.setfilterbitmap(true);
    paint.setdither(true);
    paint.setantialias(true);
    animator = valueanimator.offloat(0, 1);
    setlayertype(view.layer_type_hardware, null);
    initanimator();
  }

  private void initanimator() {
    //每次动画更新的时候,更新红包下落的坐标值
    animator.addupdatelistener(new valueanimator.animatorupdatelistener() {
      @override
      public void onanimationupdate(valueanimator animation) {
        long nowtime = system.currenttimemillis();
        float secs = (float) (nowtime - prevtime) / 1000f;
        prevtime = nowtime;
        for (int i = 0; i < redpacketlist.size(); ++i) {
          redpacket redpacket = redpacketlist.get(i);
          //更新红包的下落的位置y
          redpacket.y += (redpacket.speed * secs);

          //如果y坐标大于view的高度 说明划出屏幕,y重新设置起始位置,以及中奖属性
          if (redpacket.y > getheight()) {
            redpacket.y = 0 - redpacket.height;
            redpacket.isrealred = redpacket.isrealredpacket();
          }
          //更新红包的旋转的角度
          redpacket.rotation = redpacket.rotation
              + (redpacket.rotationspeed * secs);
        }
        invalidate();
      }
    });
    //属性动画无限循环
    animator.setrepeatcount(valueanimator.infinite);
    //属性值线性变换
    animator.setinterpolator(new linearinterpolator());
    animator.setduration(0);
  }


  /**
   *停止动画
   */
  public void stoprainnow() {
    //清空红包数据
    clear();
    //重绘
    invalidate();
    //动画取消
    animator.cancel();
  }


  /**
   * 开始动画
   */
  public void startrain() {
    //清空红包数据
    clear();
    //添加红包
    setredpacketcount(count);
    prevtime = system.currenttimemillis();
    //动画开始
    animator.start();

  }

  public void setredpacketcount(int count) {
    if (mimgids == null || mimgids.length == 0)
      return;
    for (int i = 0; i < count; ++i) {
      //获取红包原始图片
      bitmap originalbitmap = bitmapfactory.decoderesource(getresources(), mimgids[0]);
      //生成红包实体类
      redpacket redpacket = new redpacket(getcontext(), originalbitmap, speed,maxsize,minsize,mwidth);
      //添加进入红包数组
      redpacketlist.add(redpacket);
    }
  }

  /**
   * 暂停红包雨
   */
  public void pauserain() {
    animator.cancel();
  }

  /**
   * 重新开始
   */
  public void restartrain() {
    animator.start();
  }

  /**
   * 清空红包数据,并回收红包中的bitmap
   */
  private void clear() {
    for (redpacket redpacket :redpacketlist) {
      redpacket.recycle();
    }
    redpacketlist.clear();
  }


  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
    //获取自定义view的宽度
    mwidth = getmeasuredwidth();
  }

  @override
  protected void ondraw(final canvas canvas) {
    //遍历红包数组,绘制红包
    for (int i = 0; i < redpacketlist.size(); i++) {
      redpacket redpacket = redpacketlist.get(i);
      //将红包旋转redpacket.rotation角度后 移动到(redpacket.x,redpacket.y)进行绘制红包
      matrix m = new matrix();
      m.settranslate(-redpacket.width / 2, -redpacket.height / 2);
      m.postrotate(redpacket.rotation);
      m.posttranslate(redpacket.width / 2 + redpacket.x, redpacket.height / 2 + redpacket.y);
      //绘制红包
      canvas.drawbitmap(redpacket.bitmap, m, paint);
    }
  }

  @override
  public boolean ontouchevent(motionevent motionevent) {
    switch (motionevent.getaction()){
      case motionevent.action_down:
        //根据点击的坐标点,判断是否点击在红包的区域
        redpacket redpacket = isredpacketclick(motionevent.getx(), motionevent.gety());
        if (redpacket != null) {
          //如果点击在红包上,重新设置起始位置,以及中奖属性
          redpacket.y = 0 - redpacket.height;
          redpacket.isrealred = redpacket.isrealredpacket();
          if (onredpacketclicklistener != null) {
            onredpacketclicklistener.onredpacketclicklistener(redpacket);
          }
        }
        break;
      case motionevent.action_move:
        break;
      case motionevent.action_cancel:
      case motionevent.action_up:

        break;
    }
    return true;
  }

  //根据点击坐标点,遍历红包数组,看是否点击在红包上
  private redpacket isredpacketclick(float x, float y) {
    for (int i = redpacketlist.size() - 1; i >= 0; i --) {
      if (redpacketlist.get(i).iscontains(x, y)) {
        return redpacketlist.get(i);
      }
    }
    return null;
  }

  public interface onredpacketclicklistener {
    void onredpacketclicklistener(redpacket redpacket);
  }

  private onredpacketclicklistener onredpacketclicklistener;
  public void setonredpacketclicklistener(onredpacketclicklistener onredpacketclicklistener) {
    this.onredpacketclicklistener = onredpacketclicklistener;
  }
}

最后

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