Android 雪花飘落

1. 效果


这里写图片描述

2.分析和实现


2.1效果分析:

  1. 每个雪花都是随机的一张资源图片(总共五张图片),每一张图片转为bitmap,然后在onDraw中绘制;
  2. 每个雪花它的宽高是随机的范围,旋转的角度是随机的范围,加速度也是随机的;
  3. 每个雪花不拥有一个单独的动画,只用一个动画来控制所有的雪花每一帧的显示;
  4. 每个雪花的运动利用Matrix实现(setTranslate(),postRotate(),postTranslate())

2.2实现单个雪花飘落:

  先实现单个雪花的飘落,新建SingleSnowView extends View , 在他的内部新建一个静态的Snow内部类,Snow类提供一个静态的方法,初始化雪花的大小,旋转角度以及加速度然后返回一个snow实例:

public class SingleSnowView extends View {

    public static class Snow {
        // 旋转角度和速率
        public float rotation;
        public float rotationSpeed;
        // 在view中的x,y点的位置
        public float x, y;
        // 下落的速度
        public float speed;
        // 高度和宽度
        public int width, height;
        // bitmap
        public Bitmap bitmap;

        // 根据宽度存放bitmap,以便同样的宽度可以从集合中获取
        private static HashMap<Integer, Bitmap> bitmapMap = new HashMap<Integer, Bitmap>();

        public static Snow createSnow(Bitmap snowBitmap, int xRange) {
            Snow snow = new Snow();
            // 宽度在8~58之间
            snow.width = (int) (8 + (float) Math.random() * 50);
            // 根据传进来的bitmap宽高求出比例
            float hwRatio = snowBitmap.getHeight() / snowBitmap.getWidth();
            // 算出高度
            snow.height = (int) (snow.width * hwRatio);

            // x位置在[snow.width,所属View中宽度] 任意位置
            snow.x = (float) Math.random() * (xRange - snow.width);

            // 定位雪花垂直稍微偏离的顶部显示
            snow.y = 0 - (snow.height + (float) Math.random() * snow.height);

            // 每秒的下落速度 ( 雪花越大下落得越快) 
            snow.speed = 50 + snow.width * 5;

            // 片开始在-90 - 90度旋转,旋转速度到-45 - 45之间
            snow.rotation = (float) Math.random() * 180 - 90;
            snow.rotationSpeed = (float) Math.random() * 90 - 45;

            // 先根据宽度从缓存中获取
            snow.bitmap = bitmapMap.get(snow.width);
            if (snow.bitmap == null) {
                // 如果缓存中没有,创建,缓存到集合
                snow.bitmap = Bitmap.createScaledBitmap(snowBitmap, snow.width,
                        snow.height, true);
                bitmapMap.put(snow.width, snow.bitmap);
            }
            return snow;
        }

        @Override
        public String toString() {
            return "width:" + width + "  " + "height:" + height + "  "
                    + "speed:" + speed + "  " + "x:" + x + "  " + "y:" + y;
        }
    }
}

每个雪花不拥有单独的动画,只有一个动画用于更新每一帧的动画(更新位置和旋转角度),最后在onDraw()中绘制

public class SingleSnowView extends View {
    // 用于驱动所有单独的片状动画,而不是潜在的数以百计的独立的动画,我们只使用一个,然后更新所有片每一帧的动画。
    private ValueAnimator mAnimator = ValueAnimator.ofFloat(0, 1);
    // 用于追踪时间为动画和 Frames per second
    private long mPrevTime;
    // 单个雪花
    private Snow mSnow;
    private Matrix mMatrix; // 矩阵用于移动每片呈现
    // 雪花显示的bitmap
    private Bitmap mSnowBitmap;

    public SingleSnowView(Context context) {
        this(context, null);
    }

    public SingleSnowView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SingleSnowView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData();
    }

    private void initData() {
        mMatrix = new Matrix();

        mSnowBitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_1);

        mAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                // 每一帧的动画,我们计算运行时间和更新每一片的位置和旋转它的速度。
                long nowTime = System.currentTimeMillis();
                float secs = (nowTime - mPrevTime) / 1000f;
                mPrevTime = nowTime;
                // 新的位置
                mSnow.y += mSnow.speed * secs;

                if (mSnow.y > getHeight()) {
                    // 如果已经到达底部,从上面从新再来一次,重置snow y 的位置
                    mSnow.y = 0 - mSnow.height;
                }

                // 雪花旋转
                mSnow.rotation = mSnow.rotation + (mSnow.rotationSpeed * secs);

                // 请求重新去刷新绘制界面
                invalidate();
            }
        });
        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mAnimator.setDuration(2000);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 利用矩阵去实现
        mMatrix.setTranslate(-mSnow.width / 2, -mSnow.height / 2);
        mMatrix.postRotate(mSnow.rotation);
        mMatrix.postTranslate(mSnow.width / 2 + mSnow.x, mSnow.height
                / 2 + mSnow.y);
        // 绘制雪花
        canvas.drawBitmap(mSnow.bitmap, mMatrix, null);
    }

    /**
     * 开始下雪
     */
    public void startSnow(int rangeWidth) {
        if (mSnow == null) {
            mPrevTime = System.currentTimeMillis();
            mSnow = Snow.createSnow(mSnowBitmap, rangeWidth);
        }
        mAnimator.start();
    }

    /**
     * 停止下雪
     */
    public void stopSnow() {
        // 如果activity进入paused状态 , 也可以调用
        mAnimator.cancel();
    }
}

在Activity中的效果:
这里写图片描述

2.3.满屏雪花飘落

既然一个雪花可以了,那么满屏雪花飘落就很简单了,无非就是将多个雪花放入集合中,在原来的基础上修改一下就可以了:

public class SnowView extends View{
    private Bitmap mSnowBitmapOne, mSnowBitmapTwo, mSnowBitmapThree,
            mSnowBitmapFour, mSnowBitmapFive;
    private ArrayList<Bitmap> mSnowBitmaps;
    // 雪花的个数
    private int mSnowNumber = 0;
    // 最多显示雪花的个数 (75)
    private static final int MAX_SNOW_NUMBER = 75;

    private ArrayList<Snow> mSnows;// 雪花集合


    private void initData() {
        mSnows = new ArrayList<Snow>();

        mSnowBitmapOne = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_1);
        mSnowBitmapTwo = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_2);
        mSnowBitmapThree = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_3);
        mSnowBitmapFour = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_4);
        mSnowBitmapFive = BitmapFactory.decodeResource(getResources(),
                R.drawable.icon_5);

        mSnowBitmaps = new ArrayList<Bitmap>();
        mSnowBitmaps.add(mSnowBitmapOne);
        mSnowBitmaps.add(mSnowBitmapTwo);
        mSnowBitmaps.add(mSnowBitmapThree);
        mSnowBitmaps.add(mSnowBitmapFour);
        mSnowBitmaps.add(mSnowBitmapFive);

        mAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                //....
                for (Snow snow : mSnows) {
                    // ....遍历
                }
                // 请求重新去刷新绘制界面
                invalidate();
            }
        });
    }
}

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (Snow snow : mSnows) {
            // ....遍历
        }
        // TODO 下面计算帧速率
    }

    /**
     * 开始下雪
     */
    public void startSnow(int rangeWidth) {
        mPrevTime = System.currentTimeMillis();
        // 没有添加直接开始下雪时,默认添加15个
        addSnows(15, rangeWidth);
        mAnimator.start();
    }

    public int getSnowNumber() {
        return this.mSnowNumber >= MAX_SNOW_NUMBER ? MAX_SNOW_NUMBER
                : mSnowNumber;
    }

    /**
     * 添加雪花
     */
    public void addSnows(int snowNumber, int rangeWidth) {
        if (mSnowNumber < MAX_SNOW_NUMBER) {
            for (int i = 0; i < snowNumber; i++) {
                // 随机挑选 (也可以根据位置求%,等比例)
                int index = (int) (Math.random() * 5);
                Bitmap snowBitmap = mSnowBitmaps.get(index);
                Snow snow = Snow.createSnow(snowBitmap, rangeWidth);
                // 添加到集合
                mSnows.add(snow);
            }
            // 当前雪花数量叠加
            mSnowNumber += snowNumber;
        }
    }
}
2.4.最后在activity中测试
   public class MainActivity extends Activity {

    private Runnable runnableRain;
    private SnowView snowView;
    @ViewById(R.id.root_view)
    private View mRootView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_first);
        snowView = (SnowView) findViewById(R.id.snow_view);
        ViewUtils.inject(this);
    }

    @OnClick(R.id.tv_play)
    private void startSnow(){
        snowView.setVisibility(View.VISIBLE);
        snowView.startSnow(mRootView.getWidth());

        final Handler handlerRain = new Handler();
        runnableRain = new Runnable() {
            @Override
            public void run() {
                // 每隔两秒添加15个
                snowView.addSnows(15, mRootView.getWidth());
                handlerRain.postDelayed(runnableRain, 2000);
                if(snowView.getSnowNumber() > 75)
                {
                    handlerRain.removeCallbacks(runnableRain);
                }
            }
        };
        handlerRain.postDelayed(runnableRain, 0);
    }
}

快过年了,家里也下雪了,目前只想到了这样一种实现方式,不知道还有没有更好的方式
下载地址:http://download.csdn.net/detail/z240336124/9417303

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值