之前說想要寫一篇自動(dòng)補(bǔ)全的EditText,不過后來隨便寫了一下,發(fā)現(xiàn)并沒有想象中的簡單瘫析,加上最近項(xiàng)目進(jìn)度一直在趕,沒時(shí)間認(rèn)認(rèn)真真搗鼓了濒憋,看到騰訊的手機(jī)管家加速球還不錯(cuò),而且實(shí)現(xiàn)起來邏輯不是很復(fù)雜陶夜,所以這周就嘗試如何實(shí)現(xiàn)加速球這種動(dòng)畫效果跋炕。
先來看看手機(jī)管家的加速球是什么樣的
功能需求
這個(gè)控件大概需要實(shí)現(xiàn)三個(gè)部分的功能
1、在最底層畫出一個(gè)圓
2律适、畫出底層的小波浪和外層的大波浪(一開始我以為這兩個(gè)波浪是分開的,后來發(fā)現(xiàn)合在一起更加好實(shí)現(xiàn))
3.添加橫向的動(dòng)畫遏插,設(shè)置一個(gè)修改水位的縱向動(dòng)畫
4捂贿、我發(fā)現(xiàn)手機(jī)管家在水位下降之后沒有根據(jù)當(dāng)前的手機(jī)內(nèi)存耗用情況把刷新水位,雖然我不是很理解胳嘲,不過我認(rèn)為應(yīng)該加上的厂僧,添加一個(gè)回調(diào)。
知識(shí)點(diǎn)了牛,也是難點(diǎn)
首先肯定是先定義一個(gè)BallView繼承自View類了颜屠,接著重寫onDraw方法辰妙。那么在重寫onDraw方法之前,先介紹一下今天涉及到canvas的一些知識(shí)甫窟,但是都不會(huì)擴(kuò)展來講密浑,因?yàn)橐獙W(xué)好自定義控件,以下的東西都必須要掌握的:
貝塞爾曲線:關(guān)于貝塞爾曲線粗井,我想學(xué)習(xí)過自定義控件的人都會(huì)接觸到尔破,因?yàn)樗鼘?duì)于繪制曲線非常的強(qiáng)大,而我們大多數(shù)情景下繪制的東西都不是標(biāo)準(zhǔn)的圓或者矩形浇衬。
setXfermode():這是Paint里面的一個(gè)API懒构,設(shè)置圖片相交模式,對(duì)于多圖層疊的時(shí)候耘擂,這個(gè)函數(shù)就會(huì)非常的強(qiáng)大胆剧,雖然設(shè)置起來很簡單,但是要理解記住每一種模式也是相當(dāng)困難的醉冤。
離屏繪制:涉及到Canvas的圖層概念秩霍,雖然簡單來說只有兩行代碼。
接下來就是正文部分了冤灾,因?yàn)檫@個(gè)功能很簡單前域,而且對(duì)于嘗試自定義控件的朋友會(huì)覺得蠻有意思的吧,所以我就按著我自己的思路直接從代碼上來講解如何實(shí)現(xiàn)這個(gè)動(dòng)畫效果了:
1韵吨、我們知道畫筆對(duì)象其實(shí)就那么幾個(gè)匿垄,就好比如我們畫畫的時(shí)候,首先需要準(zhǔn)備好紙畫筆顏料這些零零碎碎的東西归粉,紙就是系統(tǒng)提供給我們的Canvas對(duì)象了椿疗,而至于畫筆我們需要提前準(zhǔn)備好:
不知道大家有沒有用過TakeColor這個(gè)取色工具,上面的顏色就是用這個(gè)工具取得糠悼,以后要是我們獨(dú)立開發(fā)的時(shí)候看到好的顏色搭配届榄,可以直接用這個(gè)工具就知道顏色的RGB值了。
2倔喂、接下來就是今天想重點(diǎn)講解的部分了铝条,我們都知道貝塞爾曲線能夠畫出波浪形的圓滑的路徑,但是要怎么滑還是需要斟酌斟酌席噩,像手機(jī)管家中的加速球的波浪是有內(nèi)外兩層的班缰,這樣給人一種立體感,用戶體驗(yàn)也就蹭蹭上去了悼枢。畢竟埠忘,用戶體驗(yàn)就是用戶使用過程中的感覺嗎,細(xì)節(jié)很重要啊。接下來就看看這個(gè)自定義控件是如何進(jìn)行繪圖的吧:
onDraw:
在這里可以看到一共調(diào)用了canvas的兩次draw方法莹妒,所畫即所得:
畫圓:
必須加一個(gè)背景名船,不然圖層混合后,兩個(gè)圖層重合部分的透明區(qū)域都會(huì)消失旨怠,重疊之后就看不到波浪的圓背景了渠驼。如下圖所示:
畫內(nèi)層小波浪:
? ? ?小波浪的路徑其實(shí)是有講究的,要是小波浪跟大波浪的路徑方向是一樣的运吓,即從左到右渴邦,你就會(huì)發(fā)現(xiàn)壓根看不出波浪流動(dòng)的效果了,因?yàn)榇笮〔ɡ硕纪粋€(gè)方向勻速地運(yùn)動(dòng)拘哨,那么他們相對(duì)是靜止的谋梭。這個(gè)時(shí)候我想有兩個(gè)方法,第一個(gè)方法是設(shè)置兩個(gè)速率不一樣的屬性動(dòng)畫倦青,那么我們就可以看到這兩個(gè)動(dòng)畫相對(duì)是
運(yùn)動(dòng)
的瓮床。不過這里我用的是第二個(gè)方法,用的是同一個(gè)屬性動(dòng)畫产镐,但是大波浪是從左到右隘庄,而小波浪是從右到左的。這樣不管速率問題癣亚,他們相互都是
運(yùn)動(dòng)
的丑掺。最后稍微添加一點(diǎn)水平方向的偏移量,設(shè)置rQuad的第一個(gè)參數(shù)述雾,也就是轉(zhuǎn)點(diǎn)的坐標(biāo)均小于大波浪的轉(zhuǎn)點(diǎn)坐標(biāo)街州,就可以實(shí)現(xiàn)前浪(大波浪)比后浪(小波浪)更大的效果。PS:水平偏移量跟波浪的波峰我想是有關(guān)聯(lián)的玻孟,不過具體如何這里就無從考究了唆缴。
這里再說一下具體的路徑是怎么考慮的。我們繪制組件的時(shí)候黍翎,最困擾我們的可能是Path路徑的創(chuàng)建面徽,有了路徑,剩下的就只是上色還有兩個(gè)圖層重疊時(shí)如何顯示了匣掸。
mWaveLength:一個(gè)波浪(有起有伏)的長度趟紊,如果波浪長度少于控件的寬度,那么控件就會(huì)顯示多個(gè)波浪碰酝,在這里這個(gè)值設(shè)置為控件的實(shí)際寬度霎匈,而轉(zhuǎn)點(diǎn)的x坐標(biāo)決定波峰在波浪中的水平位置。
d_value:動(dòng)畫在水平方向上的偏移量砰粹,因?yàn)檫@個(gè)偏移量是波浪的一個(gè)周期,所以for循環(huán)的起點(diǎn)是-mWaveLength+getX(),這樣不管d_value怎么改變碱璃,波浪不會(huì)"斷"掉弄痹,那么終點(diǎn)為什么設(shè)置為getWidth+mWaveLength呢,這里純粹是邏輯上視圖更加連貫而已嵌器。
畫外層大波浪:
這里大致上和獲取小波浪是一樣的肛真,不一樣的地方是,起始點(diǎn)moveTo的坐標(biāo)爽航,大波浪的起點(diǎn)是從左到右的蚓让,水位線startPosition就是我們?cè)O(shè)定的值。
該畫的部分都已經(jīng)畫完了讥珍,接下來就是動(dòng)畫部分了历极。動(dòng)畫部分難度不大,動(dòng)畫的速度就是波浪流動(dòng)的速度衷佃,所以duration不要設(shè)置的太小趟卸。總體來說都不復(fù)雜氏义,只是要實(shí)現(xiàn)流動(dòng)很自然的效果锄列,就要用點(diǎn)心了。直接貼代碼了
水平方向的動(dòng)畫(波浪流動(dòng))
豎直方向的動(dòng)畫(設(shè)置水位惯悠,水位上升下降的動(dòng)畫)
整個(gè)控件制作過程就是這樣了邻邮,難度要比EditText簡單很多很多,當(dāng)然跟那些很炫很炫的控件是沒法比的克婶,尤其是跟數(shù)據(jù)綁定在一起的控件筒严,至于手機(jī)管家上的那個(gè)小飛機(jī),pressed的時(shí)候飛機(jī)放大鸠补,點(diǎn)擊的時(shí)候飛機(jī)上升萝风,水位下降,然后飛機(jī)再從底下升起來紫岩,這些實(shí)現(xiàn)起來都不難规惰,但是這些效果這里不擴(kuò)展了。
所以最終實(shí)現(xiàn)出來的效果是
以后有空再寫一篇圖層混合模式還有View事件的分發(fā)消費(fèi)攔截泉蝌,感覺像我這樣的新手學(xué)習(xí)這兩個(gè)東西的時(shí)候總是不得要領(lǐng)歇万!得先把狠話擱下了督促一下自己才行-_-||,媽的最近壓力好大勋陪,債臺(tái)高筑啊