最近做项目的时候,需要做一个类似下红包雨的效果。经过自己的反复研究,发现使用动画是最合适的。下面贴出这种实现效果的流程

首先看一下红包雨的简单效果图

  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;//生成红包bitmapbitmap = 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;//红包起始位置yy = -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();}}
}
  1. 接下来就要需要使用自定义view来实现效果
    (1)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() {@Overridepublic 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);//更新红包的下落的位置yredPacket.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);}

(2)view的绘制

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取自定义view的宽度mWidth = getMeasuredWidth();}@Overrideprotected 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);}}

(3)设置红包雨的开始结束等事件

 /***停止动画*/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();}

(4)设置红包雨的点击事件

 @Overridepublic 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;}
  1. 在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;@Overrideprotected 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);}@Overridepublic 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() {@Overridepublic void onRedPacketClickListener(RedPacket redPacket) {redRainView1.pauseRain();ab.setCancelable(false);ab.setTitle("红包提醒");ab.setNegativeButton("继续抢红包", new DialogInterface.OnClickListener() {@Overridepublic 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() {@Overridepublic void run() {ab.show();}});}});}/*** 停止下红包雨*/private void stopRedRain() {totalmoney = 0;//金额清零redRainView1.stopRainNow();}
  1. 红包雨的布局
<?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"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@drawable/red_packets_bg" /><Buttonandroid:id="@+id/start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始"/><Buttonandroid:id="@+id/stop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:text="结束"/><TextViewandroid: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.RedPacketTestandroid: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>
  1. 自定义view的属性
<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>

最后贴出红包雨的完整代码

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() {@Overridepublic 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);//更新红包的下落的位置yredPacket.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();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取自定义view的宽度mWidth = getMeasuredWidth();}@Overrideprotected 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);}}@Overridepublic 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;}
}

Android下红包雨的实现相关推荐

  1. android 开红包动画,Android实现红包雨动画效果

    本文介绍了Android实现红包雨动画效果,分享给大家,希望对大家有帮助 红包雨 关于实现上面红包雨效果步骤如下: 1.创建一个红包实体类 public class RedPacket { publi ...

  2. 用css动画写一个下红包雨的效果

    红包雨的功能相信大家都做过,不过一般都是用js计算的,闲着无聊用css的样式写了类似的,主要用的是css的transform和animation结合.大概代码逻辑: @keyframes startH ...

  3. android实现红包雨效果,vue移动端实现红包雨效果

    这篇文章主要为大家详细介绍了vue移动端实现红包雨效果,具有一定的参考价值,可以用来参考一下. 感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 本文实例为大家分享了vue实现红包雨效果的 ...

  4. android红包雨动画,过年了,下场红包雨吧

    过年了,下场红包雨吧 过年了,使用属性动画和自定义view做了个下红包雨的动画,单机版. 效果图: 模拟器鼠标点击效果不是很好,真机上会好很多. 代码,先自定义一个红包View,画出需要的红包图片,当 ...

  5. 微信红包雨怎么抢_微信红包雨怎么发出? 微信红包雨发送技巧有哪些?

    微信红包雨怎么发出? 微信红包雨发送技巧有哪些?小编最近学会了一项技能,那就是发微信红包雨,一包多发,有对微信红包雨怎么发出感兴趣的伙伴吗,接下来小编与您分享下微信红包雨怎么发出吧. 微信红包雨怎么发 ...

  6. QQ虎年春节活动ADB自动助手(自动开星星,自动红包雨下拉,自动团圆饭,自动一笔连)

    QQ虎年春节活动ADB自动助手(自动开星星,自动红包雨下拉,自动团圆饭,自动一笔连) 项目地址:GITHUB QQ虎年春节活动ADB助手 可以进去瞧瞧,顺便别忘了:即便是登录也要给我的博客点个赞啊喂! ...

  7. android 红包雨源代码,Android 红包雨效果自定义控件

    WX20201231-181616@2x.png 思路:利用Path绘制动画轨迹,再使用PathMeasure获取轨迹中的坐标位置实时改变view的坐标完成红包动画. 封装一个红包容器view用于管理 ...

  8. android红包雨动画,SurfaceView实现红包雨平移动画

    使用SurfaceView实现简单的红包雨动画,供大家参考,具体内容如下 public class TranslateSurfaceView extends SurfaceView implement ...

  9. 前端实现红包雨功能_最全解密微信红包随机算法(含代码实现)

    code小生 一个专注大前端领域的技术平台公众号回复 Android加入安卓技术群 "  1.引言 这个系列文章已经整理了10篇,但都没有涉及到具体的红包算法实现,主要有以下两方面原因.一方 ...

最新文章

  1. 2022-2028年中国微型汽车市场投资分析及前景预测报告
  2. NR 5G 协议层服务和功能
  3. 【Ubuntu入门到精通系列讲解】常用其他命令(find ln tar apt)等速查
  4. Java设计模式之行为型:访问者模式
  5. 算法面试题-美团点评2016研发工程师编程题(二)-字符编码(哈夫曼树)
  6. 使用SAP Analytics Cloud显示新冠肺炎病毒感染人数的实时信息
  7. linux android sdk gengxinman,Android 实现增量更新
  8. 文件服务器的文件设置只读,服务器设置文件为只读
  9. 转: jvm调优参数总结
  10. String or binary data would be truncated
  11. 不是技术牛人,如何拿到国内IT巨头的Offer(转载)
  12. python写抽奖转盘_[宜配屋]听图阁
  13. 在windows xp下,一块网卡绑定多个ip
  14. hadoop常用的端口配置
  15. MySQL二十八规范数据库设计
  16. 魔兽世界WOW服务器端的模拟器【2010】
  17. IBM服务器raid5崩溃数据恢复方法
  18. 【计算机视觉】Lecture 28:跟踪简介
  19. LabView---信号发生器
  20. linux动态监控机制

热门文章

  1. rm: cannot remove `xxx’: Operation not permitted的解决方法
  2. 火水未濟 (易經大意 韓長庚)
  3. 使用selenium爬取唯品会
  4. 系统传输过程中 中文点 · 对方无法解析的问题查找
  5. 流行编曲(5)采样、小打、Pad、声场
  6. 深度爬取网易Lofter的爬虫
  7. 使用Unity的AR Foundation导出到安卓时出现黑屏问题的可能原因
  8. 学习记录 --【零基础CSS学习】03.ID选择器和类选择器
  9. 阿里云acp认证, 阿里云acp考试介绍
  10. i5 1340p和r7 7735h差距 酷睿i51340p和锐龙r77735h对比