54、Android 粒子效果之雨(下雨的效果)

核心内容:
1.绘制下雨场景的个体、雨点(直线)
2.让直线动起来
3.处理边界问题
4.构造雨点对象
5.雨点大小设置
6.速度设置和角度设置等
7.添加多个雨点
8.抽离可以在 XML 中影响的属性
9.在代码中解析样式属性并使用其控制雨点变化

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

/**
 * 基类
 */
public abstract class BaseView extends View {
    private MyThread thread;
    private boolean running = true;

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

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

    protected abstract void drawSub(Canvas canvas);
    protected abstract void logic();
    protected abstract void init();
    
    class MyThread extends Thread {
        @Override
        public void run() {
            init();
            while (running) {
                logic();
                postInvalidate();
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected final void onDraw(Canvas canvas) {
        if (thread == null) {
            thread = new MyThread();
            thread.start();
        } else {
            drawSub(canvas);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        running = false;
        super.onDetachedFromWindow();
    }

}

 

 

 

1)简单描述单个雨点的行为。

绘制下雨场景的个体,雨点(直线);

让直线动起来;

处理边界问题;

 

1 <com.rain.RainView
2         android:layout_width="match_parent"
3         android:layout_height="match_parent"
4         android:background="#ff000000"
5        />

 

 

 

 1   import android.content.Context;
 2 import android.graphics.Canvas;
 3 import android.graphics.Paint;
 4 import android.util.AttributeSet;
 5 
 6 public class RainView extends BaseView {
 8     private float startX;
 9     private float startY;
10     private float stopX;
11     private float stopY;
12     private float sizeX;
13     private float sizeY;
14     private Paint paint;
15 
16     public RainView(Context context, AttributeSet attrs) {
17         super(context, attrs);
19         // 角度。
20         sizeX = 10;
21         sizeY = 30;
22         // 以下是 初始化直线的坐标。
23         startX = 100;
24         startY = 0;
25         // 改变 角度的直线。
26         stopX = startX + sizeX;
27         stopY = startY + sizeY;
28 
29         paint = new Paint();
30         // 把直线 设置成白色。
31         paint.setColor(0xffffffff);
32     }
33 
34     public RainView(Context context) {
35         super(context);
36     }
37 
38     @Override
39     protected void drawSub(Canvas canvas) {
40         // 绘制一条直线
41         canvas.drawLine(startX, startY, stopX, stopY, paint);
42     }
43 
44     @Override   /* 让直线动起来 **/ 
45     protected void logic() {
46 
47         //倍率,通过倍率来改变速度。
48         float opt = 0.5f;
49 
50         startX += sizeX * opt;
51         stopX += sizeX * opt;
52 
53         startY += sizeY * opt;
54         stopY += sizeY * opt;
55 
56         // 当直线走出屏幕的时候,变为初始位置。
57         if (startY > getHeight()) {
58             startX = 100;
59             startY = 0;
60             stopX = startX + 10;
61             stopY = startY + 30;
62         }
63     }
64 
65 }

 

2)完善雨点行为和构造下雨场景。

构造雨点对象;

雨点大小设置;

速度设置;

角度设置等 添加多个雨点;

 

1 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
2     android:layout_width="match_parent"
3     android:layout_height="match_parent" >
4     <com.rain.RainView
5         android:layout_width="match_parent"
6         android:layout_height="match_parent"
7         android:background="#ff000000" />
8 </FrameLayout>

 

 1 public class RainView extends BaseView {
 2     // 多个 “雨点” 对象。
 3     private ArrayList<RainItem> list = new ArrayList<RainItem>();
 4     private int rainNum = 80;  // “雨点”个数。
 5 
 6     public RainView(Context context, AttributeSet attrs) {
 7         super(context, attrs);
 8     }
 9 
10     public RainView(Context context) {
11         super(context);
12     }
13 
14     @Override
15     protected void drawSub(Canvas canvas) {
16         for (RainItem item : list) {
17             item.draw(canvas);
18         }
19     }
20 
21     @Override
22     protected void logic() {
23         for (RainItem item : list) {
24             item.move();
25         }
26     }
27 
28     @Override
29     protected void init() {
30         for (int i = 0; i < rainNum; i++) {
31             RainItem item = new RainItem(getWidth(), getHeight());
32             list.add(item);
33         }
34     }
35 
36 }
 1 /**
 2  * 抽象出一个 雨点 的类
 3  */
 4 public class RainItem {
 5     private int width;
 6     private int height;
 7 
 8     private float startX;
 9     private float startY;
10     private float stopX;
11     private float stopY;
12     private float sizeX;
13     private float sizeY;
14     private float opt;  // 速率
15     private Paint paint;
16     private Random random;  // 随机数
17 
18     // 自定义“雨点”的 宽 和 高
19     public RainItem(int width, int height) {
20         this.width = width;
21         this.height = height;
22         init();
23     }
24 
25     private void init() {
26         random = new Random();
27         // 角度 "X和Y随机。
28         sizeX = 1 + random.nextInt(10);
29         sizeY = 10 + random.nextInt(20);
30         // "雨点"X和Y随机位置。
31         startX = random.nextInt(width);
32         startY = random.nextInt(height);
33         stopX = startX + sizeX;
34         stopY = startY + sizeY;
35         // 速率随机。
36         opt = 0.2f + random.nextFloat();
37         paint = new Paint();
38         
39         paint.setColor(0xffffffff);
40     }
41 
42     public void draw(Canvas canvas) {
43         canvas.drawLine(startX, startY, stopX, stopY, paint);
44     }
45 
46     public void move() {
47         startX += sizeX * opt;
48         stopX += sizeX * opt;
49 
50         startY += sizeY * opt;
51         stopY += sizeY * opt;
52 
53         if (startY > height) {
54             init();
55         }
56     }
57 
58 }

 

 

 

3)在xml中定义可以控制下雨的属性。

抽离可以在xml中影响的属性;

在代码中解析样式属性并使用其控制雨点变化;

res/values/attrs.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3     <declare-styleable name="RainView">
 4         <!-- 雨点数量 -->
 5         <attr name="rainNum" format="integer"/>
 6         <!-- 雨点大小 -->
 7         <attr name="size" format="integer"/>
 8         <!-- 雨点颜色 -->
 9         <attr name="rainColor" format="integer"/>
10         <!-- 雨点颜色随机 -->
11         <attr name="randColor" format="boolean"/>
12     </declare-styleable>
13 </resources>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:rain="http://schemas.android.com/apk/res/com.jikexueyuan.rain"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <com.rain.RainView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ff000000"
        rain:rainNum="50"
        rain:size="20"
        rain:rainColor="0xff00ff00"
        rain:randColor="true"/>
</FrameLayout>
 1 public class RainView extends BaseView {
 3     // 多个 “雨点” 对象。
 4     private ArrayList<RainItem> list = new ArrayList<RainItem>();
 5     // 以下4个值,均为默认值。
 6     private int rainNum = 80;  // “雨点”个数。
 7     private int size;
 8     private int rainColor;
 9     private boolean randColor;
10 
11     public RainView(Context context, AttributeSet attrs) {
12         super(context, attrs);
13 
14         // 加载,解析 样式属性。res/values/attrs.xml
15         TypedArray ta = context.obtainStyledAttributes(attrs,
16                 R.styleable.RainView);
17 
18         rainNum = ta.getInteger(R.styleable.RainView_rainNum, 80);
19         size = ta.getInteger(R.styleable.RainView_size, 20);
20         rainColor = ta.getInteger(R.styleable.RainView_rainColor, 0xffffffff);
21         randColor = ta.getBoolean(R.styleable.RainView_randColor, false);
22         ta.recycle();
23     }
24 
25     public RainView(Context context) {
26         super(context);
27     }
28 
29     @Override
30     protected void drawSub(Canvas canvas) {
31         for (RainItem item : list) {
32             item.draw(canvas);
33         }
34     }
35 
36     @Override
37     protected void logic() {
38         for (RainItem item : list) {
39             item.move();
40         }
41     }
42 
43     @Override
44     protected void init() {
45         for (int i = 0; i < rainNum; i++) {
46             RainItem item = new RainItem(getWidth(), getHeight(), size,
47                     rainColor, randColor);
48             list.add(item);
49         }
50     }
51 
52 }
 1 /**
 2  * 抽象出一个 雨点 的类
 3  */
 4 public class RainItem {
 5 
 6     private int width;
 7     private int height;
 8 
 9     private float startX;
10     private float startY;
11     private float stopX;
12     private float stopY;
13     private float sizeX;
14     private float sizeY;
15     private float opt;  // 速率
16     private Paint paint;
17     private Random random;  // 随机数
18 
19     private int size = 20;
20     private int color;
21     // 随机 雨点颜色 
22     private boolean randColor = false;;
23 
24     // 自定义“雨点”的 宽 和 高
25     public RainItem(int width, int height) {
26         this.width = width;
27         this.height = height;
28         init();
29     }
30 
31     public RainItem(int width, int height, int size) {
32         this.size = size;
33         this.width = width;
34         this.height = height;
35         init();
36     }
37 
38     public RainItem(int width, int height, int size, int color) {
39         this.color = color;
40         this.size = size;
41         this.width = width;
42         this.height = height;
43         init();
44     }
45 
46     public RainItem(int width, int height, int size, int color,
47             boolean randColor) {
48         this.randColor = randColor;
49         this.color = color;
50         this.size = size;
51         this.width = width;
52         this.height = height;
53         init();
54     }
55 
56     private void init() {
57         random = new Random();
58         // 角度 "X和Y随机。
59         sizeX = 1 + random.nextInt(size / 2);
60         sizeY = 10 + random.nextInt(size);
61         // "雨点"X和Y随机位置。
62         startX = random.nextInt(width);
63         startY = random.nextInt(height);
64         stopX = startX + sizeX;
65         stopY = startY + sizeY;
66         // 速率随机。
67         opt = 0.2f + random.nextFloat();
68         paint = new Paint();
69         if (randColor) {
70             // 颜色随机值。
71             int r = random.nextInt(256);
72             int g = random.nextInt(256);
73             int b = random.nextInt(256);
74 
75             paint.setARGB(255, r, g, b);
76         } else {
77             paint.setColor(color);
78         }
79     }
80 
81     public void draw(Canvas canvas) {
82         canvas.drawLine(startX, startY, stopX, stopY, paint);
83     }
84 
85     public void move() {
86         startX += sizeX * opt;
87         stopX += sizeX * opt;
88 
89         startY += sizeY * opt;
90         stopY += sizeY * opt;
91 
92         if (startY > height) {
93             init();
94         }
95     }
96 
97 }

 

DEMO下载路径:http://download.csdn.net/detail/androidsj/9279741

 

转载于:https://www.cnblogs.com/androidsj/p/4974108.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值