安卓自定義View從入門到放棄

? 前言:自打?qū)W安卓第一天起就聽過自定義View,記得當(dāng)時實習(xí)的項目里要弄一個隨機(jī)驗證碼当编,當(dāng)時百度了一份代碼丛楚,打開發(fā)現(xiàn)就一個文件族壳,叫一個xxxCodeView,放進(jìn)布局跑起來就出現(xiàn)了一個隨機(jī)的驗證碼框子趣些!當(dāng)時連Canvas仿荆,Paint都沒摸過的我覺得這份代碼的作者好牛逼啊,廖廖十來行代碼就搞定坏平。想起來真是記憶猶新赖歌!還好在后來的工作學(xué)習(xí)中時不時的從一些公眾號上有讀到一些自定義View的相關(guān)知識,慢慢的積累功茴,然后自己也會照著敲一敲。

不同的是我不喜歡一碼不差的對著源碼敲孽亲,讀懂源碼后我習(xí)慣給自己提出一點點和源碼不同的需求坎穿,然后結(jié)合自己對源碼的理解一點一點的去實現(xiàn)這個需求~ 這算是我學(xué)習(xí)自定義View的一點點小心得,在這里分享給大家先返劲!

言歸正傳玲昧,這里結(jié)合本人平時從公眾號上看來的幾個小demo稍稍改裝下做演示,簡單的介紹一下常見的幾種自定義View以及自定義View的大致方法步驟篮绿!水平有限孵延,有些地方裝逼了那也是點到即止,切勿拆穿亲配,謝謝尘应。


一惶凝、自定義View的大致步驟

? 1.新建自定義View的文件,繼承View或繼承一些系統(tǒng)自帶組件(TextView犬钢,LinearLayout)苍鲜。

? 2.復(fù)寫一些類的構(gòu)造方法,在里面獲取這個自定義View的自定義屬性(如果有)玷犹。

? 3.重寫view的onMeasure方法(這里基本上就是針對MeasureSpec.EXACTLY或者M(jìn)easureSpec.UNSPECIFIED做一些處理混滔,大多數(shù)時候代碼都是相同的,當(dāng)然具體需求具體再改歹颓,標(biāo)準(zhǔn)代碼文末放公眾號文章的鏈接坯屿,可以自己去下源碼看)

4.重寫onDraw(這個最重要了,直接決定你的自定義View呈現(xiàn)出什么東東巍扛,什么樣子)

5.其他需要重寫的方法總結(jié):

a.涉及到觸碰交互的話重寫onTouchEvent等方法(事件分發(fā)機(jī)制领跛,,电湘,這是個大課題)

b.需要刷新或者重新繪制View的時候調(diào)用postInvalidate()和Invalidate(),前者在子線程中調(diào)用~

二隔节、幾個簡單的例子,幾行關(guān)鍵代碼

示例一:進(jìn)度(時鐘寂呛,計數(shù))View

如圖所示怎诫,中間顯示計數(shù)(或者倒計時),白色被黃色覆蓋代表進(jìn)度的遞增(這里可以是顏色覆蓋贷痪,也可以是白色慢慢消失)幻妓。可以根據(jù)具體需求做修改劫拢。

實現(xiàn)思路分析:這是一個比較簡單的自定義View肉津,幾乎就是在一個Canvas上用畫筆畫N條線,自定義好線的長寬舱沧。值得一提的是將這個作為demo是為了介紹這類用Paint在Canvas上draw東西的自定義View有一個很重要的方法就是Canvas的rotate方法妹沙,圖中有54條線,不可能draw的時候找到每一條的坐標(biāo)然后去draw(當(dāng)然如果你變態(tài)到一定要去算其實是可以算:我是sin你是cos~咋倆求tan~咳咳咳熟吏!咋們是在說三角函數(shù)對吧>嗵恰)。這里我的做法是通過中心點center和View的半徑以及線的長度算出

數(shù)字正上方第一條線的起始點(x.y)?

PositionX =center; ? ? PositionY =center-out_radius; ?

然后畫出第一條線 canvas.drawLine(textPositionX, textPositionY, textPositionX, textPositionY -hightOfLine,out_paint); ?

//不要罵人牵寺,下面有注釋 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

每畫好一條線之后旋轉(zhuǎn)一定角度繼續(xù)畫悍引,另外View下方有90度的區(qū)域是沒有線的,所以旋轉(zhuǎn)的時候注意避開135~225這90度

至于根據(jù)業(yè)務(wù)邏輯動態(tài)變化顏色下圖有說明帽氓,只需要給個公開的方法設(shè)置象征進(jìn)度的progressDegree~然后調(diào)用postInvalidate()(我外部是在子線程中改變進(jìn)度趣斤,所以這里用postInvalidate,與大致步驟的第五點b 首尾呼應(yīng)一下有木有~)

核心代碼:


示例二:刮刮樂+卡片 組合View?

(其實就是抄了公眾號上的兩個demo強(qiáng)行一起用做示例撐場面而已~~這么坦誠~老Fe扎Zn不黎休?)

如圖浓领,這里展示的就是刮刮卡效果和特殊形狀的view(當(dāng)然也可以圖片實現(xiàn)玉凯,不過沒有直接自定義view來的靈活)

實現(xiàn)思路分析: 首先來說說這個刮刮卡效果

灰色的背景,手指觸摸滑動的地方變成透明镊逝,從而顯示出下方的圖片(或者其他View)壮啊。 其實說白了,就是在一張純灰色的bitmap上撑蒜,新建Paint并且設(shè)置成透明顏色歹啼,然后畫出手指的移動軌跡Path,用這個軌跡path去覆蓋掉bitmap上的灰色

即:透明色覆蓋掉灰色? ? 這里就涉及到Paint一個很重要的方法setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_IN));

關(guān)于setXfermode的參數(shù)有個經(jīng)典的圖片幫助理解座菠,這里我用的是PorterDuff.Mode.SRC_IN狸眼,也就是取 “bitmap上原色塊(純灰)和新色塊(手指劃過的地方的透明色)的交集的新色塊”? 這句話可能比較繞口,但實際你看看下面鏈接里的效果對比圖你就能理解了(看不懂浴滴?多看幾次)

http://blog.csdn.net/edisonlg/article/details/7084977

經(jīng)過分析拓萌,這個自定義View的主要成員也就有了

一個Bitmap,上面繪制灰色底色升略,和手指劃過的透明色規(guī)矩

一個Canvas微王,用來裝載這個Bitmap

一個Paint,手指的畫筆品嚣,設(shè)置覆蓋模式(取交集炕倘,顯示頂層)

一個Path,記錄手指滑動的軌跡

每次手指觸到屏幕時:

記錄下x翰撑,y罩旋,連入path,并調(diào)用invalidate刷新界面(即調(diào)用onDraw重繪)


這時候與Bitmap關(guān)聯(lián)的canvas會將透明軌跡畫到bitmap上眶诈,同時抹去軌跡下面的灰色涨醋,然后把繪制好的bitmap通過系統(tǒng)提供的canvas來展示,值得注意的是ondraw里的canvas和我們自定義的canvas有些不同逝撬,這個系統(tǒng)提供的canvas僅用來顯示最終成像的bitmap浴骂,如果直接用這個canvas來畫灰色背景再畫手指軌跡的話最終得不到想要的效果(原因目前沒搞懂,之后發(fā)現(xiàn)了再補上)

擦出功能核心的代碼就這么多了宪潮,最后還有一點就是當(dāng)擦除的面積到達(dá)一定比例后純灰層就直接移除了靠闭,這個粗一看實現(xiàn)起來完全沒有思路,坎炼,這個擦去的面積咋算呢?(同心理陰影面積一樣不好算~)拦键,但是公眾號demo里給了個解決方法挺不錯的谣光,這里直接展示出來


該方法大致的過程就是獲取bitmap的像素數(shù)據(jù),存入int數(shù)組芬为,對每個像素遍歷萄金,如果數(shù)值為0表示這個像素被擦除了蟀悦,最后累計為0的像素數(shù)目,算出百分比氧敢,超過60%就設(shè)置isCoplete為ture日戈,這樣在ondraw的時候就不會去繪制純灰和軌跡了,整個view透明孙乖,放在下面的圖片就顯示出來啦 浙炼。 當(dāng)然整個方法可以會比較耗時,手指拖動不停計算唯袄,放在子線程中異步計算比較安全~嗯弯屈,必須的

(這里我大膽的猜測一下,人臉識別算法里應(yīng)該會涉及到這個恋拷,分析像素资厉,哈哈)

刮刮樂效果到此就分析完了,當(dāng)然技術(shù)有限蔬顾,大部分東西照搬過來有些原理還是不太清楚宴偿,這里留下了兩個問題。

1.示例中這個展示效果的bitmap必須用自己new出來Canvas去綁定诀豁,最后給ondraw中系統(tǒng)提供的Canvas去展示窄刘,why?

2.實際上當(dāng)我的畫筆Paint顏色設(shè)置TRANSPARENT色不論SRC_IN還是DST_IN都實現(xiàn)了擦出的效果且叁,why都哭??逞带?

但是如果設(shè)置成別的顏色就只有SRC_IN才有效果(符合SRC_IN的效果:先畫灰層欺矫,再畫軌跡,取軌跡顏色)展氓。


卡片view效果:

篇幅問題穆趴,,這個小view很簡單遇汞,簡單說一下

1.繼承LinearLayout(或者其他)

2.在view的top和bottom畫上一排的小半圓(小三角未妹,小扇形...想畫啥都行,甚至是bitmap)

重點:確定半圓半徑空入,兩個半圓間間距(view的寬度已知络它,這樣就算出了半圓的個數(shù)circleNum)

核心代碼:同樣在ondraw中


根據(jù)小半圓的個數(shù)循環(huán)上下邊畫圓,值得注意的是啟始的x是寬度減去circleNum個圓直徑再減去circleNum-1個間距后剩下的不足一個直接的長度的一半歪赢,化戳,也就是這一段


示例三:百分比原盤


效果描述:1.外部傳入各個類別的名字,和該類別的數(shù)值 view自動計算百分比然后畫出各色扇形~

? ? ? ? ? ? ? ? ? ? 并且扇形的最外部中心出標(biāo)出名字和百分比(自定義屬性埋凯,Canvas的rotate等)

? ? ? ? ? ? ? ? ? ? 2.整體view可以拖拽轉(zhuǎn)動(觸碰事件的處理点楼,向量的計算)

? ? ? ? ? ? ? ? ? ? 3.慣性轉(zhuǎn)動(屬性動畫扫尖,差值器等)*ps: ?這里我的實現(xiàn)不太好,有bug掠廓,如果你有更好的實現(xiàn)思路可以留言哈


這個示例算是一個綜合點的自定義view换怖,包含的了前兩個簡單自定義view的大部分內(nèi)容還有前面未涉及的觸碰事件處理,廢話不多說直接上思路代碼蟀瞧。

1.實現(xiàn)基本的外形需求

數(shù)據(jù)部分很簡單沉颂,自定義一個ViewItem類,包含名字name和數(shù)值value字段黄橘,你可以再加上顏色等其他屬性

自定義View內(nèi)部存一個List<ViewItem>?datas,用來接受外部的賦值然后初始化view兆览,重點來看看如何畫這個扇形

關(guān)鍵代碼:


文字部分:


畫文字一樣是通過循環(huán)的方式一 一添加,不同的是drawText需要傳入文字的啟始位置x和底邊y塞关,所以同demo1抬探,如果每個類別都精確的去計算的話會灰常痛苦,這里的思路一樣是轉(zhuǎn)動canvas畫布帆赢,每次寫文字都寫在圓心上方半徑高度處小压,這里為了便于理解循環(huán)寫文字時canvas轉(zhuǎn)動的角度的計算,下圖給出了第一次寫 旅游(10.81%) 這段文字canvas轉(zhuǎn)動的角度a和第二次寫 ?美食(28.83%)時轉(zhuǎn)動的角度B的示意圖椰于,怠益,結(jié)合著理解下循環(huán)中計算角度的方法吧~(我知道圖里畫的是阿爾法和貝塔~~只怪win10自帶輸入法不給力啦)

到這里,效果的第一部分核心就已經(jīng)實現(xiàn)了瘾婿,onDraw里就三個方法~就得到了一個不能轉(zhuǎn)~沒鳥用的百分比原盤view

drawOutCircle(canvas);

drawInnerArc(canvas);

drawTypeText(canvas);


2.實現(xiàn)整體view拖拽轉(zhuǎn)動蜻牢,讓這個圓盤看起來不那么沒鳥用~

思考一下,要實現(xiàn)手指放在圓盤上(上下上下左右BABA)拖動的時候圓盤跟著轉(zhuǎn)動

最起碼的是不是應(yīng)該要識別一下手指拖動的方向呢偏陪?

圓盤轉(zhuǎn)動只可能是順時針或者逆時針抢呆,但是對應(yīng)不同的拖動位置和拖動方向組合有很多種,最簡單的笛谦,都是向下拖動 如果拖動的位置在圓盤左側(cè)就應(yīng)該逆時針轉(zhuǎn)抱虐,在右側(cè)就是順時針了。 那么如何來處理這個方向問題呢饥脑?正如前面寫到了恳邀,用向量!T詈洹谣沸!


***以下代碼純屬抄襲,如有雷同~~~要么我抄了你笋颤,要么我們一起抄了別人的***


這里我想起來以前做圖片旋轉(zhuǎn)的時候抄過的一段代碼(圖片被手指拖著繞著中心點旋轉(zhuǎn))


這段代碼在當(dāng)然在onTouch事件里面乳附,用到了向量交叉相乘,PointF等知識點,许溅,有點不可描述(都快還給數(shù)學(xué)老師了),大致的作用就是通過計算邊a,b,c算出邊a轉(zhuǎn)到邊c的角度newDegree秉版,這個角度就是我們要轉(zhuǎn)的角度贤重,角度的正負(fù)包含了方向。

這里在手指move的過程中清焕,這段代碼會不停的跑并蝗,所以其實每次得到的newDegree都很小,有個全局的記錄轉(zhuǎn)動度數(shù)mDegree秸妥,把newDegree累加進(jìn)去

正如最后兩行滚停,拿到了轉(zhuǎn)動角度,累加計算當(dāng)前角度粥惧,通過接口傳給外面~然后在外面調(diào)用實例的setRotation()方法

my_custom.setRotation(mDegree)來同步的實現(xiàn)view的轉(zhuǎn)動键畴。



3.手指松開后,慣性轉(zhuǎn)動效果

由于篇幅和時間(2017年4月28日00:00:24)的原因(其實主要是目前的實現(xiàn)效果自己都不能接受)暫時不寫了突雪,之后有機(jī)會再來補充起惕。



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咏删,隨后出現(xiàn)的幾起案子惹想,更是在濱河造成了極大的恐慌,老刑警劉巖督函,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘀粱,死亡現(xiàn)場離奇詭異,居然都是意外死亡辰狡,警方通過查閱死者的電腦和手機(jī)锋叨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搓译,“玉大人悲柱,你說我怎么就攤上這事⌒┘海” “怎么了豌鸡?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長段标。 經(jīng)常有香客問我涯冠,道長,這世上最難降的妖魔是什么逼庞? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任蛇更,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘派任。我一直安慰自己砸逊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布掌逛。 她就那樣靜靜地躺著师逸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豆混。 梳的紋絲不亂的頭發(fā)上呻征,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天蒿叠,我揣著相機(jī)與錄音晌涕,去河邊找鬼栈顷。 笑死,一個胖子當(dāng)著我的面吹牛鸵鸥,可吹牛的內(nèi)容都是我干的奠滑。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼脂男,長吁一口氣:“原來是場噩夢啊……” “哼养叛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宰翅,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤弃甥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汁讼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淆攻,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年嘿架,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓶珊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸彪,死狀恐怖伞芹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝉娜,我是刑警寧澤唱较,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布,位于F島的核電站召川,受9級特大地震影響南缓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荧呐,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一汉形、第九天 我趴在偏房一處隱蔽的房頂上張望纸镊。 院中可真熱鬧,春花似錦概疆、人聲如沸逗威。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庵楷。三九已至,卻和暖如春楣颠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咐蚯。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工童漩, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人春锋。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓矫膨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親期奔。 傳聞我的和親對象是個殘疾皇子侧馅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,576評論 2 349

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