过年了,使用属性动画和自定义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); } }
这样一个简易的单机版红包雨就出来了,代码不多,主要用到的就是动画的一些知识,若是是要作一个多人拼抢的红包雨游戏,还有不少细节优化,包括金额,规则,同步等等。工具