最近做项目的时候,需要做一个类似下红包雨的效果。经过自己的反复研究,发现使用动画是最合适的。下面贴出这种实现效果的流程
首先看一下红包雨的简单效果图
- 首先创建一个用来初始化红包相关的值的红包实体类
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();
}
}
}
- 接下来就要需要使用自定义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() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
long nowTime = System.currentTimeMillis();
//获取两次动画更新之间的时间,以此来计算下落的高度
float secs = (float) (nowTime - prev