Android 自定義View之下雨動(dòng)畫

效果

RainyView
RainyView

開始前先做個(gè)熱身( ??灬?? )

自己實(shí)現(xiàn)比較容易居暖,但是到了要出博客整理思路赚楚,總結(jié)要點(diǎn)的時(shí)候就撓頭威酒,不知云所以悯蝉,所以最簡(jiǎn)單的還是
Read the fucking source code

如果對(duì)安卓UI有興趣的朋友可以加我好友互相探討归形,這里有很多自定義view可以參考

思路

思路比較簡(jiǎn)單,整個(gè)view無(wú)非兩樣?xùn)|西

  • 雨滴

這里又包含兩部分動(dòng)畫鼻由,一部分是云的左右移動(dòng)動(dòng)畫暇榴,一部分是雨滴移動(dòng)動(dòng)畫
那我們這里可以自定義一些屬性,如果對(duì)自定義屬性還不太了解的同學(xué)蕉世,搜下百度哈

<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>

        <!-可同時(shí)存在的雨滴的最大數(shù)量-->
        <attr name="raindrop_max_number" format="integer"></attr>

        <!--每個(gè)雨滴之間創(chuàng)建的時(shí)間間隔-->
        <attr name="raindrop_creation_interval" format="integer"></attr>

        <!--每個(gè)雨滴的最小長(zhǎng)度-->
        <attr name="raindrop_min_length" format="integer"></attr>

        <!--每個(gè)雨滴的最大長(zhǎng)度-->
        <attr name="raindrop_max_length" format="integer"></attr>

        <!--雨滴的大小-->
        <attr name="raindrop_size" format="integer"></attr>

        <!--雨滴的最小移動(dòng)速度-->
        <attr name="raindrop_min_speed" format="float"></attr>

        <!--雨滴的最大移動(dòng)速度-->
        <attr name="raindrop_max_speed" format="float"></attr>

        <!--雨滴的斜率-->
        <attr name="raindrop_slope" format="float"></attr>
    </declare-styleable>
</resources>

畫云

云怎么畫蔼紧?

云的形狀不可勝舉,我這里只實(shí)現(xiàn)了一種簡(jiǎn)單的形狀:


RainyView

那我們?nèi)绾瓮ㄟ^(guò)畫筆將其畫出來(lái):

1.首先讨彼,我們先畫底部歉井,底部是一個(gè)圓角的矩形,通過(guò)下面方法繪制添加圓角矩形
path.addRoundRect(RectF rect, float rx, float ry, Direction dir)

RainyView

2.在該圓角的矩形的基礎(chǔ)上哈误,再畫兩個(gè)圓哩至,左邊的為小圓,右邊的為大圓蜜自,這樣就產(chǎn)生了一個(gè)最簡(jiǎn)單的云的圖形菩貌,


RainyView

在設(shè)置了以下代碼之后

paint.setStyle(Paint.Style.FILL);

云的效果如下:


RainyView

我們把這個(gè)云作為左邊的云,那么右邊的云怎么畫重荠?

很簡(jiǎn)單箭阶,因?yàn)槲覀冞@里用path來(lái)裝載了這個(gè)云的路徑,通過(guò)以下方法戈鲁,

mComputeMatrix.preTranslate(rightCloudTranslateX, -calculateRect.height() * (1 - CLOUD_SCALE_RATIO) / 2);
mComputeMatrix.postScale(CLOUD_SCALE_RATIO, CLOUD_SCALE_RATIO, rightCloudCenterX, leftCloudEndY);
mLeftCloudPath.transform(mComputeMatrix, mRightCloudPath);

將這個(gè)云的path移動(dòng)仇参,縮小,并將其路徑轉(zhuǎn)換到mRightCloudPath即可

在onDraw()的時(shí)候婆殿,調(diào)用以下方法就可以描繪路徑了

canvas.drawPath()

接下來(lái)我們來(lái)實(shí)現(xiàn)云的動(dòng)畫诈乒,我們由上面已經(jīng)了解到:

/**
 * 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);
}

該方法可以將一個(gè)path進(jìn)行matrix轉(zhuǎn)換,即矩陣轉(zhuǎn)換婆芦,因此我們可以通過(guò)方法matrix.postTranslate來(lái)實(shí)現(xiàn)平移動(dòng)畫怕磨,即創(chuàng)建一個(gè)循環(huán)動(dòng)畫喂饥,通過(guò)postTranslate來(lái)設(shè)置動(dòng)畫值就可以了,這里左邊的云在右邊的云之上肠鲫,因此先畫右邊的云员帮。

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);

畫雨滴

首先我們要知道一點(diǎn)是,所有的雨滴都是隨機(jī)產(chǎn)生的导饲,而產(chǎn)生的值捞高,可以根據(jù)上面的自定義屬性指定,也可以使用自定義的值帜消,我們先定義一個(gè)雨滴類

private class RainDrop{
    float speedX;  //雨滴x軸移動(dòng)速度
    float speedY;   //雨滴y軸移動(dòng)速度
    float xLength; //雨滴的x軸長(zhǎng)度
    float yLength; //雨滴的y軸長(zhǎng)度
    float x;        //雨滴的x軸坐標(biāo)
    float y;        //雨滴的y軸坐標(biāo)
    float slope; //雨滴的斜率
}

關(guān)于上面參數(shù)棠枉,這里畫張圖來(lái)示例:


RainyView

關(guān)于斜率
我這里開放了一個(gè)設(shè)置斜率的接口,代表雨滴的一個(gè)傾斜度泡挺,可以看到下圖的雨滴都是傾斜的辈讶,就是通過(guò)斜率來(lái)設(shè)置這個(gè)傾斜度

RainyView

斜率:表示一條直線(或曲線的切線)關(guān)于(橫)坐標(biāo)軸傾斜程度的量。它通常用直線(或曲線的切線)與(橫)坐標(biāo)軸夾角的正切娄猫,或兩點(diǎn)的縱坐標(biāo)之差與橫坐標(biāo)之差的比來(lái)表示贱除。

RainyView

該直線的斜率為k=(y1-y2)/(x1-x2)

我這里使用了固定的斜率,使所有的雨滴方向一致媳溺,如果想將其改為隨機(jī)值的同學(xué)月幌,可以下載源碼自行修改。

在創(chuàng)建雨滴對(duì)象的時(shí)候悬蔽,以下步驟使我們需要做的:

  • 斜率賦值(我這里是指定的扯躺,因此不用計(jì)算隨機(jī)斜率)
  • 計(jì)算x軸、y軸移動(dòng)速度隨機(jī)值
  • 計(jì)算雨滴長(zhǎng)度隨機(jī)值(同時(shí)計(jì)算x軸蝎困,y軸長(zhǎng)度值)
  • 計(jì)算x录语,y坐標(biāo)隨機(jī)值(為了營(yíng)造雨滴更好的出場(chǎng)效果,這里設(shè)置了y軸的起點(diǎn)坐標(biāo)為y-雨滴y軸長(zhǎng)度)

創(chuàng)建雨滴對(duì)象后禾乘,我們有了想要的參數(shù)澎埠,我們可以canvas.drawLine來(lái)畫雨滴

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參數(shù)要設(shè)置為

rainDrop.slope > 0 ? rainDrop.x + rainDrop.xLength : rainDrop.x - rainDrop.xLength

這是因?yàn)槭寂海覀兊挠甑问且恢蓖乱苿?dòng)即y是增加的蒲稳,我們上面知道斜率公式為:
k=(y1-y2)/(x1-x2)

即y1-y2肯定是大于0的,因此

當(dāng)斜率小于0的時(shí)候伍派,雨滴是這樣的江耀,即x1-x2 < 0


RainyView

當(dāng)斜率大于0的時(shí)候,雨滴是這樣的诉植,即x1-x2 > 0


RainyView

雨滴動(dòng)畫祥国,由于每一個(gè)雨滴對(duì)象參數(shù)已經(jīng)定義,在進(jìn)行動(dòng)畫的時(shí)候倍踪,只需要根據(jù)速度系宫,設(shè)置x、y軸的下一個(gè)點(diǎn)的坐標(biāo)就行了

if (rainDrop.slope >= 0) {
        rainDrop.x += rainDrop.speedX;
    }else{
        rainDrop.x -= rainDrop.speedX;
    }
rainDrop.y += rainDrop.speedY;

優(yōu)化

我們知道建车,在頻繁的創(chuàng)建雨滴的時(shí)候扩借,如果每次都創(chuàng)建新對(duì)象的話, 可能會(huì)增加不必要的內(nèi)存使用缤至,而且很容易引起頻繁的gc潮罪,甚至是內(nèi)存抖動(dòng)。

因此這里我增加了一個(gè)回收功能

//首先判斷棧中是否存在回收的對(duì)象领斥,若存在嫉到,則直接復(fù)用,若不存在月洛,則創(chuàng)建一個(gè)新的對(duì)象
private RainDrop obtainRainDrop(){
     if (mRecycler.isEmpty()){
         return new RainDrop();
     }

     return mRecycler.pop();
 }

//回收到一個(gè)棧里面何恶,若這個(gè)棧數(shù)量超過(guò)最大可顯示數(shù)量,則pop
private void recycle(RainDrop rainDrop){
    if (rainDrop == null){
        return;
    }

    if (mRecycler.size() >= mRainDropMaxNumber){
        mRecycler.pop();
    }

    mRecycler.push(rainDrop);
}

開源不易嚼黔,請(qǐng)尊重作者勞動(dòng)细层,轉(zhuǎn)載注明出處

歡迎Github follow,star以表激勵(lì)唬涧。

Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疫赎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碎节,更是在濱河造成了極大的恐慌捧搞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狮荔,死亡現(xiàn)場(chǎng)離奇詭異胎撇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)轴合,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門创坞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人受葛,你說(shuō)我怎么就攤上這事题涨。” “怎么了总滩?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵纲堵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我闰渔,道長(zhǎng)席函,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任冈涧,我火速辦了婚禮茂附,結(jié)果婚禮上正蛙,老公的妹妹穿的比我還像新娘。我一直安慰自己营曼,他們只是感情好乒验,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒂阱,像睡著了一般锻全。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上录煤,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天鳄厌,我揣著相機(jī)與錄音,去河邊找鬼妈踊。 笑死了嚎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的廊营。 我是一名探鬼主播新思,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赘风!你這毒婦竟也來(lái)了夹囚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邀窃,失蹤者是張志新(化名)和其女友劉穎荸哟,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞬捕,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞍历,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肪虎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劣砍。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扇救,靈堂內(nèi)的尸體忽然破棺而出刑枝,到底是詐尸還是另有隱情,我是刑警寧澤迅腔,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布装畅,位于F島的核電站,受9級(jí)特大地震影響沧烈,放射性物質(zhì)發(fā)生泄漏掠兄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚂夕。 院中可真熱鬧迅诬,春花似錦、人聲如沸婿牍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牍汹。三九已至,卻和暖如春柬泽,著一層夾襖步出監(jiān)牢的瞬間慎菲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工锨并, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留露该,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓第煮,卻偏偏與公主長(zhǎng)得像解幼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子包警,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容