android实现漫天雪花&下雨效果

本文介绍了如何使用Android实现漫天飞舞的雪花和下雨效果的视图,包括核心代码解析及实现过程。通过生成雪花和雨滴类,利用Canvas进行绘制,并设置动画效果,实现动态的视觉体验。

**

前言:

**

英文原文地址:https://blog.stylingandroid.com/snowfall/
中文翻译地址:http://www.open-open.com/lib/view/open1452263908573.html
国外大神实现效果youtube视频地址:https://www.youtube.com/watch?v=pk66ZziTfOw
中文翻译是open开发者经验库一位作者翻译,翻译的很好。在那篇文章也能看到实现的最原始的效果。
实现漫天飞舞的雪花下载地址:http://download.youkuaiyun.com/detail/qq_16064871/9420804
实现下雨天效果的下载地址:http://download.youkuaiyun.com/detail/qq_16064871/9420808
1、漫天飞舞的雪花主要代码
SnowView

import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.util.AttributeSet;  
import android.view.View;  

/** 
 * 雪花视图, DELAY时间重绘, 绘制NUM_SNOWFLAKES个雪花 
 */  
public class SnowView extends View {  

    private static final int NUM_SNOWFLAKES = 150; // 雪花数量  
    private static final int DELAY = 5; // 延迟  
    private SnowFlake[] mSnowFlakes; // 雪花  

    public SnowView(Context context) {  
        super(context);  
    }  

    public SnowView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  

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

    @Override   
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
        super.onSizeChanged(w, h, oldw, oldh);  
        if (w != oldw || h != oldh) {  
            initSnow(w, h);  
        }  
    }  

    private void initSnow(int width, int height) {  
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // 抗锯齿  
        paint.setColor(Color.WHITE); // 白色雪花  
        paint.setStyle(Paint.Style.FILL); // 填充;  
        mSnowFlakes = new SnowFlake[NUM_SNOWFLAKES];  
        //mSnowFlakes所有的雪花都生成放到这里面  
        for (int i = 0; i < NUM_SNOWFLAKES; ++i) {  
            mSnowFlakes[i] = SnowFlake.create(width, height, paint);  
        }  
    }  

    @Override   
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        //for返回SnowFlake  
        for (SnowFlake s : mSnowFlakes) {  
            //然后进行绘制  
            s.draw(canvas);  
        }  
        // 隔一段时间重绘一次, 动画效果  
        getHandler().postDelayed(runnable, DELAY);  
    }  

    // 重绘线程  
    private Runnable runnable = new Runnable() {  
        @Override  
        public void run() {  
            //自动刷新  
            invalidate();  
        }  
    };  
}  

SnowFlake

package com.example.snowflake.view;  

import com.example.snowflake.RandomGenerator;  

import android.graphics.Canvas;  
import android.graphics.Paint;  
import android.graphics.Point;  

/** 
 * 雪花的类, 移动, 移出屏幕会重新设置位置. 
 */  
public class SnowFlake {  
    // 雪花的角度  
    private static final float ANGE_RANGE = 0.1f; // 角度范围  
    private static final float HALF_ANGLE_RANGE = ANGE_RANGE / 2f; // 一般的角度  
    private static final float HALF_PI = (float) Math.PI / 2f; // 半PI  
    private static final float ANGLE_SEED = 25f; // 角度随机种子  
    private static final float ANGLE_DIVISOR = 10000f;  
    // 雪花的移动速度  
    private static final float INCREMENT_LOWER = 2f;  
    private static final float INCREMENT_UPPER = 4f;  

    // 雪花的大小  
    private static final float FLAKE_SIZE_LOWER = 7f;  
    private static final float FLAKE_SIZE_UPPER = 20f;  

    private final RandomGenerator mRandom; // 随机控制器  
    private final Point mPosition; // 雪花位置  
    private float mAngle; // 角度  
    private final float mIncrement; // 雪花的速度  
    private final float mFlakeSize; // 雪花的大小  
    private final Paint mPaint; // 画笔  

    private SnowFlake(RandomGenerator random, Point position, float angle, float increment, float flakeSize, Paint paint) {  
        mRandom = random;  
        mPosition = position;  
        mIncrement = increment;  
        mFlakeSize = flakeSize;  
        mPaint = paint;  
        mAngle = angle;  
    }  

    public static SnowFlake create(int width, int height, Paint paint) {  
        RandomGenerator random = new RandomGenerator();  
        int x = random.getRandom(width);  
        int y = random.getRandom(height);  
        Point position = new Point(x, y);  
        float angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;  
        float increment = random.getRandom(INCREMENT_LOWER, INCREMENT_UPPER);  
        float flakeSize = random.getRandom(FLAKE_SIZE_LOWER, FLAKE_SIZE_UPPER);  
        return new SnowFlake(random, position, angle, increment, flakeSize, paint);  
    }  

    // 绘制雪花  
    public void draw(Canvas canvas) {  
        int width = canvas.getWidth();  
        int height = canvas.getHeight();  
        move(width, height);  
        canvas.drawCircle(mPosition.x, mPosition.y, mFlakeSize, mPaint);  
    }  

    // 移动雪花  
    private void move(int width, int height) {  
        //x水平方向,那么需要晃动,主要设置这个值就可以,现在取消晃动了  
        //如果 mPosition.x不加上后面那个值,就不会晃动了  
        double x = mPosition.x + (mIncrement * Math.cos(mAngle));  
        //y是竖直方向,就是下落  
        double y = mPosition.y + (mIncrement * Math.sin(mAngle));  

        mAngle += mRandom.getRandom(-ANGLE_SEED, ANGLE_SEED) / ANGLE_DIVISOR;  
        //这个是设置雪花位置,如果在很短时间内刷新一次,就是连起来的动画效果  
        mPosition.set((int) x, (int) y);  

        // 移除屏幕, 重新开始  
        if (!isInside(width, height)) {  
            // 重置雪花  
            reset(width);  
        }  
    }  

    // 判断是否在其中  
    private boolean isInside(int width, int height) {  
        int x = mPosition.x;  
        int y = mPosition.y;  
        return x > mFlakeSize -5 && x + mFlakeSize <= width && y >= -mFlakeSize - 1 && y - mFlakeSize < height;  
    }  

    // 重置雪花  
    private void reset(int width) {  
        mPosition.x = mRandom.getRandom(width);  
        mPosition.y = (int) (-mFlakeSize - 1); // 最上面  
        mAngle = mRandom.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;  
    }  
}  

2、实现下雨天效果代码
RainView

package com.example.raindrop.view;  

import com.example.raindrop.R;  

import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Paint;  
import android.util.AttributeSet;  
import android.view.View;  

/** 
 * 雨滴视图, DELAY时间重绘, 绘制NUM_SNOWFLAKES个雨滴 
 */  
public class RainView extends View {  

    private static final int NUM_SNOWFLAKES = 150; // 雨滴数量  
    private static final int DELAY = 5; // 延迟  
    private RainFlake[] mSnowFlakes; // 雨滴  

    public RainView(Context context) {  
        super(context);  
    }  

    public RainView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  

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

    @Override   
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
        super.onSizeChanged(w, h, oldw, oldh);  
        if (w != oldw || h != oldh) {  
            initSnow(w, h);  
        }  
    }  

    private void initSnow(int width, int height) {  
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // 抗锯齿  
        paint.setColor(getResources().getColor(R.color.colorWater)); // 雨滴的颜色  
        paint.setStyle(Paint.Style.FILL); // 填充;  
        mSnowFlakes = new RainFlake[NUM_SNOWFLAKES];  
        //mSnowFlakes所有的雨滴都生成放到这里面  
        for (int i = 0; i < NUM_SNOWFLAKES; ++i) {  
            mSnowFlakes[i] = RainFlake.create(width, height, paint);  
        }  
    }  

    @Override   
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
        //for返回SnowFlake  
        for (RainFlake s : mSnowFlakes) {  
            //然后进行绘制  
            s.draw(canvas);  
        }  
        // 隔一段时间重绘一次, 动画效果  
        getHandler().postDelayed(runnable, DELAY);  
    }  

    // 重绘线程  
    private Runnable runnable = new Runnable() {  
        @Override  
        public void run() {  
            //自动刷新  
            invalidate();  
        }  
    };  
}  

RainFlake

package com.example.raindrop.view;  

import com.example.raindrop.RandomGenerator;  

import android.graphics.Canvas;  
import android.graphics.Paint;  

/** 
 * 雨滴的类, 移动, 移出屏幕会重新设置位置. 
 */  
public class RainFlake {  

    // 雨滴的移动速度  
    private static final float INCREMENT_LOWER = 6f;  
    private static final float INCREMENT_UPPER = 8f;  

    // 雨滴的大小  
    private static final float FLAKE_SIZE_LOWER = 2f;  
    private static final float FLAKE_SIZE_UPPER = 5f;  

    private final float mIncrement; // 雨滴的速度  
    private final float mFlakeSize; // 雨滴的大小  
    private final Paint mPaint; // 画笔  

    private Line mLine; // 雨滴  

    private RandomGenerator mRandom;  

    private RainFlake(RandomGenerator random,Line line, float increment, float flakeSize, Paint paint) {  
        mRandom = random;  
        mLine = line;  
        mIncrement = increment;  
        mFlakeSize = flakeSize;  
        mPaint = paint;  
    }  

    //生成雨滴  
    public static RainFlake create(int width, int height, Paint paint) {  
        RandomGenerator random = new RandomGenerator();  
        int [] nline;  
        nline = random.getLine(width, height);  

        Line line = new Line(nline[0], nline[1], nline[2], nline[3]);  
        float increment = random.getRandom(INCREMENT_LOWER, INCREMENT_UPPER);  
        float flakeSize = random.getRandom(FLAKE_SIZE_LOWER, FLAKE_SIZE_UPPER);  
        return new RainFlake(random,line, increment, flakeSize, paint);  
    }  

    // 绘制雨滴  
    public void draw(Canvas canvas) {  
        int width = canvas.getWidth();  
        int height = canvas.getHeight();  
        drawLine(canvas, width, height);  
    }  

    /** 
     * 改成线条,类似于雨滴效果 
     * @param canvas 
     * @param width 
     * @param height 
     */  
    private void drawLine(Canvas canvas, int width, int height) {  
        //设置线宽  
      mPaint.setStrokeWidth(mFlakeSize);  
        //y是竖直方向,就是下落  
      double y1 = mLine.y1 + (mIncrement * Math.sin(1.5));  
      double y2 = mLine.y2 + (mIncrement * Math.sin(1.5));  

      //这个是设置雨滴位置,如果在很短时间内刷新一次,就是连起来的动画效果  
       mLine.set(mLine.x1,(int) y1,mLine.x2 ,(int) y2);  

        if (!isInsideLine(height)) {  
            resetLine(width,height);  
        }  

        canvas.drawLine(mLine.x1, mLine.y1, mLine.x2, mLine.y2, mPaint);  
    }  

    // 判断是否在其中  
    private boolean isInsideLine(int height) {  
        return mLine.y1 < height && mLine.y2 < height;  
    }  

    // 重置雨滴  
    private void resetLine(int width, int height) {  
        int [] nline;  
        nline = mRandom.getLine(width, height);  
        mLine.x1 = nline[0];  
        mLine.y1 = nline[1];  
        mLine.x2 = nline[2];  
        mLine.y2 = nline[3];  
    }  

}  

效果开始前先做个热身( ˘&bull;灬&bull;˘ )自己实现比较容易,但是到了要出博客整理思路,总结要点的时候就挠头,不知云所以,所以最简单的还是&nbsp;Read the fucking source code如果对安卓UI有兴趣的朋友可以加我好友互相探讨,这里有很多自定义view可以参考思路思路比较简单,整个view无非两样东西云雨滴这里又包含两部分动画,一部分是云的左右移动动画,一部分是雨滴移动动画 那我们这里可以自定义一些属性,如果对自定义属性还不太了解的同学,搜下百度哈&lt;resources&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;declare-styleable&nbsp;name=&quot;RainyView&quot;&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--雨滴的颜色--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_color&quot;&nbsp;format=&quot;color&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--左边云的颜色--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;left_cloud_color&quot;&nbsp;format=&quot;color&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--右边云的颜色--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;right_cloud_color&quot;&nbsp;format=&quot;color&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-可同时存在的雨滴的最大数量--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_max_number&quot;&nbsp;format=&quot;integer&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--每个雨滴之间创建的时间间隔--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_creation_interval&quot;&nbsp;format=&quot;integer&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--每个雨滴的最小长度--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_min_length&quot;&nbsp;format=&quot;integer&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--每个雨滴的最大长度--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_max_length&quot;&nbsp;format=&quot;integer&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--雨滴的大小--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_size&quot;&nbsp;format=&quot;integer&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--雨滴的最小移动速度--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_min_speed&quot;&nbsp;format=&quot;float&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--雨滴的最大移动速度--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_max_speed&quot;&nbsp;format=&quot;float&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--雨滴的斜率--&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;attr&nbsp;name=&quot;raindrop_slope&quot;&nbsp;format=&quot;float&quot;&gt;&lt;/attr&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;/declare-styleable&gt; &lt;/resources&gt;画云云怎么画?云的形状不可胜举,我这里只实现了一种简单的形状:那我们如何通过画笔将其画出来:1.首先,我们先画底部,底部是一个圆角的矩形,通过下面方法绘制添加圆角矩形path.addRoundRect(RectF rect, float rx, float ry, Direction dir)&nbsp;2.在该圆角的矩形的基础上,再画两个圆,左边的为小圆,右边的为大圆,这样就产生了一个最简单的云的图形,&nbsp;在设置了以下代码之后paint.setStyle(Paint.Style.FILL);云的效果如下:我们把这个云作为左边的云,那么右边的云怎么画?很简单,因为我们这里用path来装载了这个云的路径,通过以下方法,mComputeMatrix.preTranslate(rightCloudTranslateX,&nbsp;-calculateRect.height()&nbsp;*&nbsp;(1&nbsp;-&nbsp;CLOUD_SCALE_RATIO)&nbsp;/&nbsp;2); mComputeMatrix.postScale(CLOUD_SCALE_RATIO,&nbsp;CLOUD_SCALE_RATIO,&nbsp;rightCloudCenterX,&nbsp;leftCloudEndY); mLeftCloudPath.transform(mComputeMatrix,&nbsp;mRightCloudPath);将这个云的path移动,缩小,并将其路径转换到mRightCloudPath即可在onDraw()的时候,调用以下方法就可以描绘路径了canvas.drawPath()接下来我们来实现云的动画,我们由上面已经了解到:/** &nbsp;*&nbsp;Transform&nbsp;the&nbsp;points&nbsp;in&nbsp;this&nbsp;path&nbsp;by&nbsp;matrix,&nbsp;and&nbsp;write&nbsp;the&nbsp;answer &nbsp;*&nbsp;into&nbsp;dst.&nbsp;If&nbsp;dst&nbsp;is&nbsp;null,&nbsp;then&nbsp;the&nbsp;the&nbsp;original&nbsp;path&nbsp;is&nbsp;modified. &nbsp;* &nbsp;*&nbsp;@param&nbsp;matrix&nbsp;The&nbsp;matrix&nbsp;to&nbsp;apply&nbsp;to&nbsp;the&nbsp;path &nbsp;*&nbsp;@param&nbsp;dst&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;transformed&nbsp;path&nbsp;is&nbsp;written&nbsp;here.&nbsp;If&nbsp;dst&nbsp;is&nbsp;null, &nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;then&nbsp;the&nbsp;the&nbsp;original&nbsp;path&nbsp;is&nbsp;modified &nbsp;*/ public&nbsp;void&nbsp;transform(Matrix&nbsp;matrix,&nbsp;Path&nbsp;dst)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;dstNative&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(dst&nbsp;!=&nbsp;null)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dst.isSimplePath&nbsp;=&nbsp;false; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dstNative&nbsp;=&nbsp;dst.mNativePath; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;nTransform(mNativePath,&nbsp;matrix.native_instance,&nbsp;dstNative); }该方法可以将一个path进行matrix转换,即矩阵转换,因此我们可以通过方法matrix.postTranslate来实现平移动画,即创建一个循环动画,通过postTranslate来设置动画值就可以了,这里左边的云在右边的云之上,因此先画右边的云。mComputeMatrix.reset(); mComputeMatrix.postTranslate((mMaxTranslationX&nbsp;/&nbsp;2)&nbsp;*&nbsp;mRightCloudAnimatorValue,&nbsp;0); mRightCloudPath.transform(mComputeMatrix,&nbsp;mComputePath); canvas.drawPath(mComputePath,&nbsp;mRightCloudPaint); mComputeMatrix.reset(); mComputeMatrix.postTranslate(mMaxTranslationX&nbsp;*&nbsp;mLeftCloudAnimatorValue,&nbsp;0); mLeftCloudPath.transform(mComputeMatrix,&nbsp;mComputePath); canvas.drawPath(mComputePath,&nbsp;mLeftCloudPaint);画雨滴首先我们要知道一点是,所有的雨滴都是随机产生的,而产生的值,可以根据上面的自定义属性指定,也可以使用自定义的值,我们先定义一个雨滴类private&nbsp;class&nbsp;RainDrop{ &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;speedX;&nbsp;&nbsp;//雨滴x轴移动速度 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;speedY;&nbsp;&nbsp;&nbsp;//雨滴y轴移动速度 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;xLength;&nbsp;//雨滴的x轴长度 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;yLength;&nbsp;//雨滴的y轴长度 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;x;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//雨滴的x轴坐标 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;y;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//雨滴的y轴坐标 &nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;slope;&nbsp;//雨滴的斜率 }关于上面参数,这里画张图来示例:关于斜率&nbsp;我这里开放了一个设置斜率的接口,代表雨滴的一个倾斜度,可以看到下图的雨滴都是倾斜的,就是通过斜率来设置这个倾斜度&nbsp;斜率:表示一条直线(或曲线的切线)关于(横)坐标轴倾斜程度的量。它通常用直线(或曲线的切线)与(横)坐标轴夹角的正切,或两点的纵坐标之差与横坐标之差的比来表示。该直线的斜率为k=(y1-y2)/(x1-x2)我这里使用了固定的斜率,使所有的雨滴方向一致,如果想将其改为随机值的同学,可以下载源码自行修改。在创建雨滴对象的时候,以下步骤使我们需要做的:斜率赋值(我这里是指定的,因此不用计算随机斜率)计算x轴、y轴移动速度随机值计算雨滴长度随机值(同时计算x轴,y轴长度值)计算x,y坐标随机值(为了营造雨滴更好的出场效果,这里设置了y轴的起点坐标为y-雨滴y轴长度)创建雨滴对象后,我们有了想要的参数,我们可以canvas.drawLine来画雨滴canvas.drawLine(rainDrop.x,&nbsp;rainDrop.y, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rainDrop.slope&nbsp;&gt;&nbsp;0&nbsp;?&nbsp;rainDrop.x&nbsp; &nbsp;rainDrop.xLength&nbsp;:&nbsp;rainDrop.x&nbsp;-&nbsp;rainDrop.xLength, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rainDrop.y&nbsp; &nbsp;rainDrop.yLength, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mRainPaint);这里需要注意以下,为什么canvas.drawLine中的stopX参数要设置为rainDrop.slope&nbsp;&gt;&nbsp;0&nbsp;?&nbsp;rainDrop.x&nbsp; &nbsp;rainDrop.xLength&nbsp;:&nbsp;rainDrop.x&nbsp;-&nbsp;rainDrop.xLength这是因为,我们的雨滴是一直往下移动即y是增加的,我们上面知道斜率公式为:k=(y1-y2)/(x1-x2)即y1-y2肯定是大于0的,因此当斜率小于0的时候,雨滴是这样的,即x1-x2 &lt; 0&nbsp;当斜率大于0的时候,雨滴是这样的,即x1-x2 &gt; 0&nbsp;雨滴动画,由于每一个雨滴对象参数已经定义,在进行动画的时候,只需要根据速度,设置x、y轴的下一个点的坐标就行了if&nbsp;(rainDrop.slope&nbsp;&gt;=&nbsp;0)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rainDrop.x&nbsp; =&nbsp;rainDrop.speedX; &nbsp;&nbsp;&nbsp;&nbsp;}else{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rainDrop.x&nbsp;-=&nbsp;rainDrop.speedX; &nbsp;&nbsp;&nbsp;&nbsp;} rainDrop.y&nbsp; =&nbsp;rainDrop.speedY;优化我们知道,在频繁的创建雨滴的时候,如果每次都创建新对象的话, 可能会增加不必要的内存使用,而且很容易引起频繁的gc,甚至是内存抖动。因此这里我增加了一个回收功能//首先判断栈中是否存在回收的对象,若存在,则直接复用,若不存在,则创建一个新的对象 private&nbsp;RainDrop&nbsp;obtainRainDrop(){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(mRecycler.isEmpty()){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;RainDrop(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;mRecycler.pop(); &nbsp;} //回收到一个栈里面,若这个栈数量超过最大可显示数量,则pop private&nbsp;void&nbsp;recycle(RainDrop&nbsp;rainDrop){ &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(rainDrop&nbsp;==&nbsp;null){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(mRecycler.size()&nbsp;&gt;=&nbsp;mRainDropMaxNumber){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mRecycler.pop(); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;mRecycler.push(rainDrop); }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值