溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用Android實現(xiàn)紅包雨動畫效果

發(fā)布時間:2021-04-16 11:33:16 來源:億速云 閱讀:605 作者:小新 欄目:移動開發(fā)

這篇文章主要介紹如何使用Android實現(xiàn)紅包雨動畫效果,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

紅包雨

如何使用Android實現(xiàn)紅包雨動畫效果

關于實現(xiàn)上面紅包雨效果步驟如下:

1.創(chuàng)建一個紅包實體類

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) {
    //獲取一個顯示紅包大小的倍數(shù)
    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();
  }

  /**
   * 判斷當前點是否包含在區(qū)域內(nèi)
   */
  public boolean isContains(float x, float y) {
    //稍微擴大下點擊的區(qū)域
    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]隨機出的數(shù)字是2的倍數(shù) 為中獎紅包
    if (num % 2 == 0) {
      money = num*2;//中獎金額
      return true;
    }
    return false;
  }

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

上面就紅包實體類的源碼,重點就是在創(chuàng)建紅包實體的時候,初始化紅包相關的值,如生成紅包圖片,圖片的寬高,紅包初始位置,下落速度等。比較簡單。

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屬性,如下落紅包數(shù)量,下落的基礎速度,以及紅包圖片的最大最小范圍
    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);
    //創(chuàng)建一個屬性動畫,通過屬性動畫來控制刷新紅包下落的位置
    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();
      }
    });
    //屬性動畫無限循環(huán)
    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) {
    //遍歷紅包數(shù)組,繪制紅包
    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() {
    //清空紅包數(shù)據(jù)
    clear();
    //重繪
    invalidate();
    //動畫取消
    animator.cancel();
  }


  /**
   * 開始動畫
   */
  public void startRain() {
    //清空紅包數(shù)據(jù)
    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);
      //添加進入紅包數(shù)組
      redpacketlist.add(redPacket);
    }
  }

  /**
   * 暫停紅包雨
   */
  public void pauseRain() {
    animator.cancel();
  }

  /**
   * 重新開始
   */
  public void restartRain() {
    animator.start();
  }

  /**
   * 清空紅包數(shù)據(jù),并回收紅包中的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:
        //根據(jù)點擊的坐標點,判斷是否點擊在紅包的區(qū)域
        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;
  }

  //根據(jù)點擊坐標點,遍歷紅包數(shù)組,看是否點擊在紅包上
  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("繼續(xù)搶紅包", 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("很遺憾,下次繼續(xù)努力!");
        }
        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;//紅包數(shù)量
  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<>();//紅包數(shù)組

  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();
      }
    });
    //屬性動畫無限循環(huán)
    animator.setRepeatCount(ValueAnimator.INFINITE);
    //屬性值線性變換
    animator.setInterpolator(new LinearInterpolator());
    animator.setDuration(0);
  }


  /**
   *停止動畫
   */
  public void stopRainNow() {
    //清空紅包數(shù)據(jù)
    clear();
    //重繪
    invalidate();
    //動畫取消
    animator.cancel();
  }


  /**
   * 開始動畫
   */
  public void startRain() {
    //清空紅包數(shù)據(jù)
    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);
      //添加進入紅包數(shù)組
      redpacketlist.add(redPacket);
    }
  }

  /**
   * 暫停紅包雨
   */
  public void pauseRain() {
    animator.cancel();
  }

  /**
   * 重新開始
   */
  public void restartRain() {
    animator.start();
  }

  /**
   * 清空紅包數(shù)據(jù),并回收紅包中的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) {
    //遍歷紅包數(shù)組,繪制紅包
    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:
        //根據(jù)點擊的坐標點,判斷是否點擊在紅包的區(qū)域
        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;
  }

  //根據(jù)點擊坐標點,遍歷紅包數(shù)組,看是否點擊在紅包上
  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;
  }
}

以上是“如何使用Android實現(xiàn)紅包雨動畫效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關知識,歡迎關注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI