Android 自定义View之下雨动画

6
回复
457
查看
[复制链接]

5

主题

7

帖子

97

安币

程序猿

Rank: 2

QQ
发表于 2018-10-12 11:44:19 | 显示全部楼层 |阅读模式

效果

RainyView

RainyView

开始前先做个热身( ˘•灬•˘ )

自己实现比较容易,但是到了要出博客整理思路,总结要点的时候就挠头,不知云所以,所以最简单的还是 

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>

画云

云怎么画?

云的形状不可胜举,我这里只实现了一种简单的形状:

RainyView

那我们如何通过画笔将其画出来:

1.首先,我们先画底部,底部是一个圆角的矩形,通过下面方法绘制添加圆角矩形

<span style="white-space:pre"></span>`

<span style="white-space:pre"></span>path.addRoundRect(RectF rect, float rx, float ry, Direction dir)

<span style="white-space:pre"></span>`

<span style="white-space:pre"></span>RainyView

2.在该圆角的矩形的基础上,再画两个圆,左边的为小圆,右边的为大圆,这样就产生了一个最简单的云的图形,

<span style="white-space:pre"></span>RainyView

在设置了以下代码之后


paint.setStyle(Paint.Style.FILL);

云的效果如下:

RainyView

我们把这个云作为左边的云,那么右边的云怎么画?

很简单,因为我们这里用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; //雨滴的斜率

}

关于上面参数,这里画张图来示例:

RainyView

关于斜率

我这里开放了一个设置斜率的接口,代表雨滴的一个倾斜度,可以看到下图的雨滴都是倾斜的,就是通过斜率来设置这个倾斜度

RainyView

斜率:表示一条直线(或曲线的切线)关于(横)坐标轴倾斜程度的量。它通常用直线(或曲线的切线)与(横)坐标轴夹角的正切,或两点的纵坐标之差与横坐标之差的比来表示。

RainyView

该直线的斜率为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

RainyView

当斜率大于0的时候,雨滴是这样的,即x1-x2 > 0

RainyView

雨滴动画,由于每一个雨滴对象参数已经定义,在进行动画的时候,只需要根据速度,设置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);

}

开源不易,请尊重作者劳动,转载注明出处

欢迎Github follow,star以表激励。

Github

1

主题

9378

帖子

2551

安币

Android大神

Rank: 6Rank: 6

发表于 2018-10-14 19:33:33 | 显示全部楼层
感谢分享,楼主V5~

62

主题

9770

帖子

911

安币

代码手工艺人

学海无涯

Rank: 4

QQ达人

发表于 2018-10-14 19:33:43 | 显示全部楼层
感谢分享,楼主V5~

27

主题

9687

帖子

1873

安币

Android大神

Rank: 6Rank: 6

发表于 2018-10-14 19:37:45 | 显示全部楼层
帮帮顶顶!!

451

主题

1134

帖子

1958

安币

手工艺人

发表于 2018-10-14 19:39:56 | 显示全部楼层
不错不错,楼主辛苦了。。。

1

主题

9194

帖子

2912

安币

Android大神

Rank: 6Rank: 6

发表于 2018-10-14 19:41:57 | 显示全部楼层
感谢分享,安卓巴士有你更精彩:lol

579

主题

1280

帖子

3264

安币

手工艺人

发表于 2018-10-14 19:42:17 | 显示全部楼层
楼主是好人,回个帖会有安币吗?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )