Android實(shí)現(xiàn)紅包雨動(dòng)畫效果
本文介紹了Android實(shí)現(xiàn)紅包雨動(dòng)畫效果,分享給大家,希望對(duì)大家有幫助
紅包雨

關(guān)于實(shí)現(xiàn)上面紅包雨效果步驟如下:
1.創(chuàng)建一個(gè)紅包實(shí)體類
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) {
//獲取一個(gè)顯示紅包大小的倍數(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;
//初始化該紅包的初始旋轉(zhuǎn)角度
rotation = (float) Math.random() * 180 - 90;
//初始化該紅包的旋轉(zhuǎn)速度
rotationSpeed = (float) Math.random() * 90 - 45;
//初始化是否為中獎(jiǎng)紅包
isRealRed = isRealRedPacket();
}
/**
* 判斷當(dāng)前點(diǎn)是否包含在區(qū)域內(nèi)
*/
public boolean isContains(float x, float y) {
//稍微擴(kuò)大下點(diǎn)擊的區(qū)域
return this.x-50 < x && this.x +50 + width > x
&& this.y-50 < y && this.y+50 + height > y;
}
/**
* 隨機(jī) 是否為中獎(jiǎng)紅包
*/
public boolean isRealRedPacket() {
Random random = new Random();
int num = random.nextInt(10) + 1;
//如果[1,10]隨機(jī)出的數(shù)字是2的倍數(shù) 為中獎(jiǎng)紅包
if (num % 2 == 0) {
money = num*2;//中獎(jiǎng)金額
return true;
}
return false;
}
/**
* 回收?qǐng)D片
*/
public void recycle() {
if (bitmap!= null && !bitmap.isRecycled()){
bitmap.recycle();
}
}
}
上面就紅包實(shí)體類的源碼,重點(diǎn)就是在創(chuàng)建紅包實(shí)體的時(shí)候,初始化紅包相關(guān)的值,如生成紅包圖片,圖片的寬高,紅包初始位置,下落速度等。比較簡(jiǎn)單。
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ù)量,下落的基礎(chǔ)速度,以及紅包圖片的最大最小范圍
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)建一個(gè)屬性動(dòng)畫,通過(guò)屬性動(dòng)畫來(lái)控制刷新紅包下落的位置
animator = ValueAnimator.ofFloat(0, 1);
//繪制view開啟硬件加速
setLayerType(View.LAYER_TYPE_HARDWARE, null);
//初始化屬性動(dòng)畫
initAnimator();
}
private void initAnimator() {
//每次動(dòng)畫更新的時(shí)候,更新紅包下落的坐標(biāo)值
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
long nowTime = System.currentTimeMillis();
//獲取兩次動(dòng)畫更新之間的時(shí)間,以此來(lái)計(jì)算下落的高度
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坐標(biāo)大于view的高度 說(shuō)明劃出屏幕,y重新設(shè)置起始位置,以及中獎(jiǎng)屬性
if (redPacket.y > getHeight()) {
redPacket.y = 0 - redPacket.height;
redPacket.isRealRed = redPacket.isRealRedPacket();
}
//更新紅包的旋轉(zhuǎn)的角度
redPacket.rotation = redPacket.rotation
+ (redPacket.rotationSpeed * secs);
}
//重繪
invalidate();
}
});
//屬性動(dòng)畫無(wú)限循環(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);
//將紅包旋轉(zhuǎn)redPacket.rotation角度后 移動(dòng)到(redPacket.x,redPacket.y)進(jìn)行繪制紅包
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);
}
}
紅包雨動(dòng)畫開始結(jié)束
/**
*停止動(dòng)畫
*/
public void stopRainNow() {
//清空紅包數(shù)據(jù)
clear();
//重繪
invalidate();
//動(dòng)畫取消
animator.cancel();
}
/**
* 開始動(dòng)畫
*/
public void startRain() {
//清空紅包數(shù)據(jù)
clear();
//添加紅包
setRedpacketCount(count);
prevTime = System.currentTimeMillis();
//動(dòng)畫開始
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]);
//生成紅包實(shí)體類
RedPacket redPacket = new RedPacket(getContext(), originalBitmap, speed,maxSize,minSize,mWidth);
//添加進(jìn)入紅包數(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();
}
紅包點(diǎn)擊事件
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
//根據(jù)點(diǎn)擊的坐標(biāo)點(diǎn),判斷是否點(diǎn)擊在紅包的區(qū)域
RedPacket redPacket = isRedPacketClick(motionEvent.getX(), motionEvent.getY());
if (redPacket != null) {
//如果點(diǎn)擊在紅包上,重新設(shè)置起始位置,以及中獎(jiǎng)屬性
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ù)點(diǎn)擊坐標(biāo)點(diǎn),遍歷紅包數(shù)組,看是否點(diǎn)擊在紅包上
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;
}
關(guān)于自定義紅包雨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("中獎(jiǎng)金額: " + 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="結(jié)束"
/>
<TextView
android:id="@+id/money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="中獎(jiǎng)金額:"
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;//屬性動(dòng)畫,用該動(dòng)畫來(lái)不斷改變紅包下落的坐標(biāo)值
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() {
//每次動(dòng)畫更新的時(shí)候,更新紅包下落的坐標(biāo)值
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坐標(biāo)大于view的高度 說(shuō)明劃出屏幕,y重新設(shè)置起始位置,以及中獎(jiǎng)屬性
if (redPacket.y > getHeight()) {
redPacket.y = 0 - redPacket.height;
redPacket.isRealRed = redPacket.isRealRedPacket();
}
//更新紅包的旋轉(zhuǎn)的角度
redPacket.rotation = redPacket.rotation
+ (redPacket.rotationSpeed * secs);
}
invalidate();
}
});
//屬性動(dòng)畫無(wú)限循環(huán)
animator.setRepeatCount(ValueAnimator.INFINITE);
//屬性值線性變換
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(0);
}
/**
*停止動(dòng)畫
*/
public void stopRainNow() {
//清空紅包數(shù)據(jù)
clear();
//重繪
invalidate();
//動(dòng)畫取消
animator.cancel();
}
/**
* 開始動(dòng)畫
*/
public void startRain() {
//清空紅包數(shù)據(jù)
clear();
//添加紅包
setRedpacketCount(count);
prevTime = System.currentTimeMillis();
//動(dòng)畫開始
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]);
//生成紅包實(shí)體類
RedPacket redPacket = new RedPacket(getContext(), originalBitmap, speed,maxSize,minSize,mWidth);
//添加進(jìn)入紅包數(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);
//將紅包旋轉(zhuǎn)redPacket.rotation角度后 移動(dòng)到(redPacket.x,redPacket.y)進(jìn)行繪制紅包
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ù)點(diǎn)擊的坐標(biāo)點(diǎn),判斷是否點(diǎn)擊在紅包的區(qū)域
RedPacket redPacket = isRedPacketClick(motionEvent.getX(), motionEvent.getY());
if (redPacket != null) {
//如果點(diǎn)擊在紅包上,重新設(shè)置起始位置,以及中獎(jiǎng)屬性
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ù)點(diǎn)擊坐標(biāo)點(diǎn),遍歷紅包數(shù)組,看是否點(diǎn)擊在紅包上
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;
}
}
最后
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 教你一步步實(shí)現(xiàn)Android微信自動(dòng)搶紅包
- Android輔助功能AccessibilityService與搶紅包輔助
- Android實(shí)現(xiàn)QQ搶紅包插件
- Android中微信搶紅包插件原理解析及開發(fā)思路
- Android搶紅包插件實(shí)現(xiàn)原理淺析
- Android輔助功能實(shí)現(xiàn)自動(dòng)搶紅包(附源碼)
- 分享Android微信紅包插件
- Android實(shí)現(xiàn)微信自動(dòng)搶紅包的程序
- Android微信自動(dòng)搶紅包插件優(yōu)化和實(shí)現(xiàn)
- SurfaceView實(shí)現(xiàn)紅包雨平移動(dòng)畫
相關(guān)文章
Android實(shí)現(xiàn)仿iOS菊花加載圈動(dòng)畫效果
iOS上有一個(gè)UIActivityIndicator的控件,就是俗稱轉(zhuǎn)菊花的控件,一般UI設(shè)計(jì)師會(huì)按照iOS的風(fēng)格來(lái)出設(shè)計(jì)稿,也要求使用這種Loading效果,本文將具體的講述如何實(shí)現(xiàn)這種效果,感興趣的朋友可以參考下2021-05-05
Android SeekBar 自定義thumb旋轉(zhuǎn)動(dòng)畫效果
某些音樂(lè)播放或者視頻播放的界面上,資源還在加載時(shí),進(jìn)度條的原點(diǎn)(thumb)會(huì)顯示一個(gè)轉(zhuǎn)圈的效果。這篇文章主要介紹了Android SeekBar 自定義thumb thumb旋轉(zhuǎn)動(dòng)畫效果,需要的朋友可以參考下2021-11-11
Android 系統(tǒng)服務(wù)TelecomService啟動(dòng)過(guò)程原理分析
這篇文章主要介紹了Android 系統(tǒng)服務(wù)TelecomService啟動(dòng)過(guò)程原理分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
安卓(Android) 監(jiān)聽按鍵長(zhǎng)按事件
本文主要介紹Android 中監(jiān)聽按鍵的長(zhǎng)按事件,對(duì)按鍵機(jī)制進(jìn)行詳解,并附有代碼實(shí)例,具有參考價(jià)值,希望能幫到有需要的小伙伴2016-07-07

