您好,登錄后才能下訂單哦!
這篇文章主要介紹如何使用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è)資訊頻道!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。