Android水波加載圖標(biāo)的loading效果

使用貝塞爾曲線實(shí)現(xiàn)水波紋加載icon的功能
源碼地址

貝塞爾曲線

安卓path提供了以下幾種方法處理貝賽爾曲線:

quadTo(float x1, float y1, float x2, float y2)
rQuadTo(float dx1, float dy1, float dx2, float dy2)
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

前兩個(gè)方法是用于繪制二階貝塞爾曲線,后兩個(gè)是繪制三節(jié)貝塞爾曲線稿存,兩個(gè)一組喳整,區(qū)別是rQuadTo和rCubicTo用的是相對(duì)距離屿脐,相對(duì)于起始點(diǎn)的距離(起始點(diǎn)即為調(diào)用時(shí)path當(dāng)前的點(diǎn),可以通過(guò)path.moveTo(float x1, float y1)的方法移動(dòng)到你想要的位置)苗分,而另外兩個(gè)用的則是絕對(duì)距離。
關(guān)于參數(shù),以quadTo為例锌钮,P1(float x1, float y1)構(gòu)成的點(diǎn)就是貝賽爾曲線的控制點(diǎn),P2(float x2, float y2)為終點(diǎn)引矩,起點(diǎn)P0就是當(dāng)前path所在的點(diǎn)梁丘;


二階貝塞爾曲線(圖片來(lái)源網(wǎng)絡(luò),侵刪)

三階貝塞爾曲線多一個(gè)控制點(diǎn)脓魏,所以方法中間多了一組控制點(diǎn)兰吟。


三階貝塞爾曲線(圖片來(lái)源網(wǎng)絡(luò),侵刪)

我們這里的水波紋可以看作是正弦函數(shù)茂翔,通過(guò)兩個(gè)二階貝塞爾曲線可以實(shí)現(xiàn)混蔼,水波紋的上移則通過(guò)改變P0點(diǎn)的y值,水平移動(dòng)通過(guò)改變P0點(diǎn)的x值
定義變量
var waveLength: Float = 0f //水波寬度珊燎,即二階貝塞爾P0-P2的距離
var waveHeight: Float = 0f  //波峰控制點(diǎn)的偏移量惭嚣,即P1的y值與P0的y值的差值
var distance = 0f     //橫向偏移量
var waveY: Float = 0f //當(dāng)前P0的y坐標(biāo)

因?yàn)橐瓷先ナ且恢背粋€(gè)方向移動(dòng),所以需要曲線移動(dòng)的最大邊距是正弦函數(shù)的x軸悔政,所以distance是一個(gè)在0和waveLength之間變化的數(shù)
在onSizeChanged根據(jù)當(dāng)前View大小自適應(yīng)初始化相關(guān)參數(shù)

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        width = getWidth().toFloat()
        height = getHeight().toFloat()
        waveY = height   //P0從底部開(kāi)始晚吞,所以初始值是View的height
        waveHeight = height * 0.12f  //取height的12%作為控制點(diǎn)的偏移量
        waveLength = width * 2 / 3f    //取整個(gè)View寬度的2/3作為一個(gè)波的寬度
    }

這里我們使用取整個(gè)View寬度的2/3作為一個(gè)波的寬度,因此兩個(gè)曲線就足夠了,但是考慮到我們橫向移動(dòng)的范圍有一個(gè)曲線的寬度谋国,所以我們需要3個(gè)曲線
貝塞爾曲線的path,根據(jù)當(dāng)前l(fā)oading的高度以及水平偏移量動(dòng)態(tài)計(jì)算

wavePath.moveTo(-distance, waveY) //當(dāng)前幀繪制第一個(gè)貝塞爾曲線的P0點(diǎn)
                                  //后面的貝塞爾曲線的起始點(diǎn)是上一個(gè)曲線的終點(diǎn)
for (i in 0..2) {
            wavePath.rQuadTo(
                waveLength / 2, //控制點(diǎn)在半個(gè)周期中間
                //這里通過(guò)(-1.0).pow(i.toDouble())依次生成交替的正負(fù)1槽地,控制波峰或者是波谷,
                waveHeight * (-1.0).pow(i.toDouble()).toFloat(),
                waveLength,
                0f
            )
        }

distance先以waveLength / 50f的間隔增大到一個(gè)曲線寬度再以相同的間隔減為0,就達(dá)到了曲線一直在橫向滾動(dòng)的效果捌蚊,看上去是個(gè)水波

    distanceTemp += waveLength / 50f
        val residual = distanceTemp % waveLength
        //當(dāng)橫坐標(biāo)移動(dòng)到一個(gè)周期之后則反向移動(dòng)
        distance =
            if ((distanceTemp / waveLength).toInt() and 1 == 1)
                waveLength - residual
            else residual
        waveY -= height / 100f

如果這個(gè)時(shí)候我們把這個(gè)waterPath畫出來(lái)集畅,我們能得到這樣的效果——一條上升的水波浪


waterPath.gif

利用path裁剪Bitmap

現(xiàn)在水波浪曲線已經(jīng)有了,我們只需要將曲線與底部圍成一個(gè)封閉的path缅糟,然后再配合我們的Bitmap就能實(shí)現(xiàn)水波紋加載圖片的動(dòng)態(tài)效果

//圍成封閉的Path
wavePath.lineTo(width, height)
        wavePath.lineTo(0f, height)
        wavePath.close()

創(chuàng)建一個(gè)和View等大的Bitmap并綁定到Canvas上挺智,我們知道通過(guò)Bitmap創(chuàng)建的Canvas,對(duì)這個(gè)畫布做的所有操作實(shí)際上都作用在了這個(gè)傳進(jìn)去的Bitmap上窗宦,所以我們通過(guò)waveCanvas以SRC_IN的模式組合path和Icon(我們想要加載的圖標(biāo)赦颇,也是一個(gè)Bitmap),之后再拿到處理后的waveBitmap赴涵,將其繪制在OnDraw的參數(shù)Canvas上媒怯,就達(dá)到了效果

val waveBitmap = Bitmap.createBitmap(width.toInt(), height.toInt(), Bitmap.Config.ARGB_8888)
val waveCanvas = Canvas(waveBitmap)

waveCanvas.drawPath(wavePath, mPaint)
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
waveCanvas.drawBitmap(getBitmapFromDrawable(), 0f, 0f, mPaint)

到此waveBitmap已經(jīng)是一個(gè)icon和path,最后draw這個(gè)bitmap,大功告成

canvas.drawBitmap(waveBitmap, 0f, 0f, mPaint)

效果
icon.gif

附上源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末句占,一起剝皮案震驚了整個(gè)濱河市沪摄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纱烘,老刑警劉巖杨拐,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異擂啥,居然都是意外死亡哄陶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門哺壶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屋吨,“玉大人,你說(shuō)我怎么就攤上這事山宾≈寥牛” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵资锰,是天一觀的道長(zhǎng)敢课。 經(jīng)常有香客問(wèn)我,道長(zhǎng)绷杜,這世上最難降的妖魔是什么直秆? 我笑而不...
    開(kāi)封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮鞭盟,結(jié)果婚禮上圾结,老公的妹妹穿的比我還像新娘。我一直安慰自己齿诉,他們只是感情好筝野,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布晌姚。 她就那樣靜靜地躺著,像睡著了一般遗座。 火紅的嫁衣襯著肌膚如雪舀凛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天途蒋,我揣著相機(jī)與錄音,去河邊找鬼馋记。 笑死号坡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梯醒。 我是一名探鬼主播宽堆,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茸习!你這毒婦竟也來(lái)了畜隶?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤号胚,失蹤者是張志新(化名)和其女友劉穎籽慢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猫胁,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箱亿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弃秆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片届惋。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖菠赚,靈堂內(nèi)的尸體忽然破棺而出脑豹,到底是詐尸還是另有隱情,我是刑警寧澤衡查,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布瘩欺,位于F島的核電站,受9級(jí)特大地震影響峡捡,放射性物質(zhì)發(fā)生泄漏击碗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一们拙、第九天 我趴在偏房一處隱蔽的房頂上張望稍途。 院中可真熱鬧,春花似錦砚婆、人聲如沸械拍。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坷虑。三九已至甲馋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迄损,已是汗流浹背定躏。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芹敌,地道東北人痊远。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氏捞,于是被迫代替她去往敵國(guó)和親碧聪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354