手撸一个物体下落的控件,实现雪花飘落效果

效果图:

圣诞登录页.gif

参考文章:

Android自定义View——从零开始实现雪花飘落效果
感谢原文作者,不仅实现了效果,并且写得非常详细,还做了优化。笔者参考原文作者的源码,做了一点修改,实现了效果并加入了项目中。不过都大同小异,下面笔者会将学习和制作中的难点和注意点分享给大家。

提炼与分享:

1. 如何实现简单的物体下落:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(fallObjects.size()>0){
            for (int i=0;iparentHeight || presentX<-bitmap.getWidth() || presentX>parentWidth+bitmap.getWidth()){
            reset();
        }
    }
    private void moveY(){
        presentY += presentSpeed;
    }
    private void moveX(){
        presentX += defaultWindSpeed * Math.sin(angle);
        if(isAngleChange){
            angle += (float) (random.nextBoolean()?-1:1) * Math.random() * 0.0025;
        }
    }
    private void reset(){
        presentY = -objectHeight;
        randomSpeed();
        randomWind();//记得重置一下初始角度,不然雪花会越下越少(因为角度累加会让雪花越下越偏)
    }

  首先是Y轴控制竖直下落,初始的Y轴坐标是通过屏幕高度取随机值-屏幕高度来确定的。这样物体会从不同的位置下落,在相同速度的情况下,也能在不同的时间进入屏幕。
  然后是X轴,正常的雪花肯定不是竖直下落,也不是折线下落,而是弧形,View中采用的sin函数的-Pi到Pi之间的值绘制弧形。x轴的初始位置通过对屏幕宽度做随机值确定。
  最后在物体到底屏幕底部,或者超过屏幕左右边界时,重置物体(reset方法)。需要重置的是y轴的点,以及物体的速度,当然还有我们模拟的风力,后面会单独说。

2. 为什么要使用Builder建造者模式

  其实原文已经讲得很仔细了,我们物体会有大量的参数和对应的行为方法,为了提高代码的可读性,我们将物体提取出来,作为一个单独的类。而大量的参数采用普通的构造方法去构造,实在是不知道,传入的参数究竟代表什么。而建造者模式能够解决这个问题。

        FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.snowflake));
        FallObject fallObject = builder
                .setSpeed(8,true)
                .setSize(80,80,true)
                .setWind(5,true)
                .build();
        //初始化一个雪球样式的fallObject
        ((FallingView)findViewById(R.id.fallingView)).addFallObject(fallObject,60);//添加60个雪球对象

  在这个项目中,我们将所有与下落物体相关的方法和属性全部封装在FallObject中,并且提供Builder内部类实例化。而我们的View则仅仅需要作为一个画布,提供添加下落对象的方法,重复的绘制物体即可。至于绘制的对象是要下落还是要旋转,都与View没有关系了。

3. 绘制图片并且控制其大小

  绘制图片在View中是有提供方法的:canvas.drawBitmap(bitmap,presentX,presentY,null);从方法中可以看到,我们需要的是bitmap的图片,那么,我们在修改图片大小之前,还需要先将drawable转化为bitmap。

    /**
     * drawable图片资源转bitmap
     * @param drawable
     * @return
     */
    public static Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(
                drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(),
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                        : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

  上面是drawable转化为bitmap的代码,简单来讲就是Bitmap的采用的是工厂模式创建一个bitmap空对象,然后通过drawable将图片图像画在bitmap对象中。

    /**
     * 改变bitmap的大小
     * @param bitmap 目标bitmap
     * @param newW 目标宽度-
     * @param newH 目标高度
     * @return
     */
    public static Bitmap changeBitmapSize(Bitmap bitmap, int newW, int newH) {
        int oldW = bitmap.getWidth();
        int oldH = bitmap.getHeight();
        // 计算缩放比例
        float scaleWidth = ((float) newW) / oldW;
        float scaleHeight = ((float) newH) / oldH;
        // 取得想要缩放的matrix参数
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        // 得到新的图片
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, oldW, oldH, matrix, true);
        return bitmap;
    }

  上面是改变图片显示大小的方法。就是直接对bitmap的缩放操作返回新的bitmao对象。需要注意的是为了保证不失帧,新的宽高度需要按原大小等比例缩放。

4. 如何引入风的概念

    /**
     * 随机风的风向和风力大小比例,即随机物体初始下落角度
     */
    private void randomWind(){
        if(isAngleChange){
            angle = (float) ((random.nextBoolean()?-1:1) * Math.random() * initWindLevel /50);
        }else {
            angle = (float) initWindLevel /50;
        }
        //限制angle的最大最小值
        if(angle>HALF_PI){
            angle = HALF_PI;
        }else if(angle<-HALF_PI){
            angle = -HALF_PI;
        }
    }

  正常情况下,我们的雪花不会是直线下落的,而是有轻微的弧度飘落,我们通过改变X轴的方式来实现水平位移,但是为了保证位移的平滑,我们采用了sin正弦函数计算x轴的值,采用-π/2到π/2的弧线值作为函数的角度。这个曲线值是[-1,1],可以实现雪花自由的左右弧线移动。initWindLevel是我们模拟的风力,风力值越大,雪花飘落的弧度就越大。

在用户在xml中使用,Activity中实例化:

  FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.snowflake));
        FallObject fallObject = builder
                .setSpeed(8,true)
                .setSize(80,80,true)
                .setWind(5,true)
                .build();
        //初始化一个雪球样式的fallObject
        ((FallingView)findViewById(R.id.fallingView)).addFallObject(fallObject,60);//添加50个雪球对象
 

源码:

wusyLibrary://wusylibrary/src/main/java/com/wusy/wusylibrary/view/FallingView/
扫描文章末尾的二维码,关注笔者的公众号:饮水思源|wusy,回复Android源码获取。感谢你的支持。

End

笔者的Github Blog,希望各位大大提意见,点个star,谢谢
传送门:WusyBlog

求互粉互赞,互赞所有文章可以私聊我。哈哈,希望我们的原创文章能让更多朋友看到,一起变强。

笔者新开通了微信公众号——饮水思源|wusy 计划持续运营,每日为您分享Android干货、原创文章。微信扫描下方的二维码关注我,开发学习路上不迷路。谢谢各位


手撸一个物体下落的控件,实现雪花飘落效果_第1张图片
饮水思源|wusy.jpg

你可能感兴趣的:(手撸一个物体下落的控件,实现雪花飘落效果)