Android实现红包雨动画效果

时间:2021-05-20

本文介绍了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; }}

最后

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

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章