7

过年了,下场红包雨吧

过年了,使用属性动画和自定义view做了个下红包雨的动画,单机版。

效果图:

图片描述

模拟器鼠标点击效果不是很好,真机上会好很多.

代码,先自定义一个红包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的设置:


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:

/**
 * 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);
    }
}

这样一个简易的单机版红包雨就出来了,代码很少,主要用到的就是动画的一些知识,如果是要做一个多人拼抢的红包雨游戏,还有很多细节优化,包括金额,规则,同步等等。


why_rookie
96 声望4 粉丝

Android开发