過年了,使用屬性動畫和自定義view作了個下紅包雨的動畫,單機版。java
效果圖:canvas
模擬器鼠標點擊效果不是很好,真機上會好不少.後端
代碼,先自定義一個紅包View,畫出須要的紅包圖片,當紅包被點擊時換一張表示拆開紅包的圖片,其餘的都寫在注視裏.數組
/** * Created by weng on 2018/1/27. */ public class RedPacketView extends View { private float amount;//每一個紅包的金額 private boolean isClicked;//判斷紅包是否被點擊過,點擊以後再點不會觸發效果 private Bitmap redPacketBitmap;//紅包對應的bitmap private Paint paint; private int width = 50; private int height= 50; public RedPacketView(Context context) { this(context, null); } public RedPacketView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RedPacketView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); //設置默認的爲拆開的紅包圖片 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.red_packet); redPacketBitmap = Bitmap.createScaledBitmap(bitmap, (int) dpToPixel(width), (int) dpToPixel(height), false); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //這裏直接設置了大小,若是想更精確一些,也能夠設置爲屏幕寬度和高度的百分比,這樣適配會更好一些 setMeasuredDimension((int) dpToPixel(width), (int) dpToPixel(height)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(redPacketBitmap, 0, 0, paint); } //設置紅包被拆開的圖片 private void setRedPacketBitmap() { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.money); redPacketBitmap = Bitmap.createScaledBitmap(bitmap, (int)dpToPixel(width), (int)dpToPixel(height), false); invalidate(); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN : //獲取點擊事件,若是未被拆開,點擊以後設置拆開的圖片 if (!isClicked()) { setClicked(true); setRedPacketBitmap(); } break; } return true; } public float getAmount() { return amount; } public void setAmount(float amount) { this.amount = amount; } public boolean isClicked() { return isClicked; } public void setClicked(boolean clicked) { isClicked = clicked; } }
接下來是Activity的設置:dom
public class MainActivity extends AppCompatActivity { private int[] mSize;//保存屏幕尺寸 private FrameLayout mFrameLayout; private int mTotalAmount;//保存搶到的紅包總金額 private int mTotalAccount = 100;//紅包的個數 private int mCurrentAccount;//當前生成的個數,一旦達到總的紅包個數,中止繼續生成紅包 private static final int mInitY = 60; private int mDuration = 3000;//每一個動畫的默認時長 private int mDelay = 300;//每次生成紅包的默認間隔 private TimeInterpolator[] mInterpolators;//保存不一樣的插值器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSize = getWindowWidthAndHeight(this);//獲取屏幕的寬和高 initInterpolator();//設置好插值器數組,而後隨機設置插值器,紅包的動畫就會速度不一樣的效果 mFrameLayout = findViewById(R.id.rl_container); startAnimation();//開始動畫 } //循環生成紅包,使用static,不持有Activity的引用 private static Handler mHandler = new Handler(); private void startAnimation() { mHandler.postDelayed(new Runnable() { @Override public void run() { if (mCurrentAccount > mTotalAccount) { return; }else { mCurrentAccount++; } final RedPacketView redPacketView = new RedPacketView(MainActivity.this); redPacketView.setAmount(getRandomFloat(50));//每一個紅包的額度暫定爲50,其實這個應該是後端傳過來的數據,這裏省略 mFrameLayout.addView(redPacketView);//把紅包添加進來 redPacketView.setX(getInitialX()); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(redPacketView, "translationY", -dpToPixel(mInitY),mSize[1] + dpToPixel(mInitY)); //隨機設置插值器 objectAnimator.setInterpolator(mInterpolators[getRandomInt(3)]); //設置動畫時長,也能夠設置成隨機的 objectAnimator.setDuration(mDuration); //給動畫添加監聽器,當動畫結束時,主要作兩件事1.判斷紅包是否被拆開,如拆開,增長搶到的紅包金額 //2.remove子view objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (redPacketView.isClicked()) { mTotalAmount += redPacketView.getAmount(); Log.e("MainActivity", "搶到的紅包總額 " + mTotalAmount); } mFrameLayout.removeView(redPacketView);//一旦動畫結束,當即remove,讓系統及時回收 } }); objectAnimator.start(); mHandler.postDelayed(this, mDelay); } }, mDelay); } //紅包生成時的初始X座標,這裏設置爲0-屏幕width-紅包width private float getInitialX(){ int max = (int)(mSize[0] - dpToPixel(50)); Random random = new Random(); float ranNum = random.nextInt(max); return ranNum; } private void initInterpolator () { //這裏設置了屬性動畫的不一樣的插值器,第一個是線性Interpolator,勻速,第二個持續加速,第三個先加速再減速 //若是對動畫還有不瞭解的,推薦博客:http://hencoder.com/page/2/ mInterpolators = new BaseInterpolator[] {new LinearInterpolator(), new AccelerateInterpolator(), new AccelerateDecelerateInterpolator()}; } }
最後是工具類Utils:ide
/** * Created by why on 2018/1/29. */ public class Utils { //dp轉px public static float dpToPixel(float dp) { DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); return dp * displayMetrics.density; } //獲取屏幕尺寸 public static int[] getWindowWidthAndHeight(Context context) { WindowManager windowManager = ((Activity)context).getWindowManager(); return new int[] {windowManager.getDefaultDisplay().getWidth(), windowManager.getDefaultDisplay().getHeight() }; } public static float getRandomFloat(int max) { Random random = new Random(); return random.nextInt(max); } public static int getRandomInt(int max) { Random random = new Random(); return random.nextInt(max); } }
這樣一個簡易的單機版紅包雨就出來了,代碼不多,主要用到的就是動畫的一些知識,若是是要作一個多人拼搶的紅包雨遊戲,還有不少細節優化,包括金額,規則,同步等等。工具