在項(xiàng)目開(kāi)發(fā)之中,當(dāng)android原生的view不能滿足我們的需求的時(shí)候,我們往往都是通過(guò)自定義view去重新繪制出符合的view,其實(shí)自定義view說(shuō)難不難,說(shuō)容易也不容易,本篇文章是我自己在學(xué)習(xí)自定義view的時(shí)候總結(jié)的一些內(nèi)容
步驟:
1.在res/values下創(chuàng)建一個(gè)attrs.xml,用來(lái)存放我們的屬性(名字可以隨便取,但最好就是規(guī)范)
2.在構(gòu)造方法里,獲取屬性并且賦值
3.繼承view,并且重寫onMeasure(),onDraw()方法
4.在布局文件中,包名+類名引入view使用
注意事項(xiàng):
1.最好實(shí)現(xiàn)3個(gè)構(gòu)造方法,為了擴(kuò)展
2.如果有跟用戶交互的功能或者防止跟其他view發(fā)生事件沖突的時(shí)候,需要做onTonchEvent處理
3.最好適應(yīng)view的三種測(cè)量模式,AT_MOST,EXACTLY,UNSPECIFIED,如果你這個(gè)view是固定寬高的話,那可以考慮不做適應(yīng)
4.在布局文件中,記得是包名+類名,并且如果有自定義屬性的話,需要加上命名空間
onMeasure
該方法是測(cè)量該view的在布局中的大小,有三種模式,AT_MOST,EXACTLY,UNSPECIFIED
EXACTLY
是指當(dāng)我們?yōu)関iew設(shè)定了明確的值所對(duì)應(yīng)的模式,如100dp,match_parent(該值是指view占滿全屏,相當(dāng)于屏幕的值,所以是個(gè)明確值)
AT_MOST
意思是說(shuō)最多不能超過(guò)某個(gè)值,如wrap_content,當(dāng)設(shè)置了wrap_content時(shí),意味著說(shuō),這個(gè)view是由內(nèi)容去決定大小,假如是個(gè)textview,那么它可能由字體去決定大小,那么這個(gè)字體會(huì)有個(gè)大小,則說(shuō)這個(gè)view的大小不能超過(guò)這個(gè)字體的大小,假如該view被包裹在一個(gè)父控件中,則說(shuō)這個(gè)view不能超過(guò)父空間的大小
UNSPECIFIED
這個(gè)模式是說(shuō)我沒(méi)有限制view的大小,你愛(ài)多大就多大,多小就多小,這種模式多數(shù)出現(xiàn)在listview,recyclerview,ScrollView等可以隨意改變itemview的大小的控件中,這里控件的itemview是不規(guī)則的,例如listview中某個(gè)view高點(diǎn),某個(gè)小點(diǎn),recyclerview多item布局的不規(guī)則布局等都是這種模式
onDraw
這方法就是將view繪制出來(lái),好了,說(shuō)了差不多了,開(kāi)始把,根據(jù)步驟開(kāi)始
attrs.xml文件:
很簡(jiǎn)單,就一個(gè)背景的屬性,format的意思是說(shuō)應(yīng)該用取值類型,有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag,這里對(duì)幾個(gè)不熟悉的介紹下:
1.demension:尺寸,相當(dāng)于文字的大小
如: <attr name="titleTextSize" format="dimension" /> ,使用的時(shí)候就可以 android:textSize = "42sp"
2.reference:參照某個(gè)資源ID
如<attr name = "background" format = "reference" /> ,布局文件就可以這樣使用:android:background = "@drawable/圖片ID"
3.fraction:百分?jǐn)?shù)
如<attr name = "pivotX" format = "fraction" /> ,使用的時(shí)候可以這樣 android:pivotX = "200%"
4.enum:枚舉值,這個(gè)值在定義屬性的時(shí)候比較特殊點(diǎn),要這樣寫
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr> ,使用的時(shí)候這樣
<LinearLayout
android:orientation = "vertical或horizontal"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent" />
自定義view
一個(gè)最簡(jiǎn)單的view就寫好了,這里先不寫onMeasure方法,我們總的流程就是,繼承view之后,通過(guò)obtainStyledAttributes()這個(gè)方法獲取到屬性表,并且獲得屬性之后賦值給我們的paint,然后通過(guò)canvas去繪制出我們的view,最后別忘了,在布局文件中引入
我們還可以設(shè)置它的背景,首先需要引入我們的命名空間
xmlns:app="http://schemas.android.com/apk/res-auto"
然后調(diào)用屬性
初步的view已經(jīng)寫好了,但就這樣完事了嗎?然而并沒(méi)有,前面說(shuō)過(guò),自定義view是需要重寫onMeasure方法的,如果沒(méi)用,假如我們現(xiàn)在去設(shè)置android:padding屬性,你會(huì)發(fā)現(xiàn),它根本沒(méi)有任何變化,為什么呢?因?yàn)檫@就是我們并沒(méi)有對(duì)它的模式進(jìn)行處理,也就是說(shuō),其實(shí)它的warp_content和match_parent是一樣的,好了,現(xiàn)在我們就處理下
首先是對(duì)padding的處理,這個(gè)還是挺簡(jiǎn)單的,當(dāng)我們?cè)O(shè)置了它的padding之后,我們就用它的寬高減去padding就可以了
這里設(shè)置了android:padding="20dp"
現(xiàn)在我們來(lái)重寫onMeasure方法,我們先想下,我們?cè)诓季治募O(shè)定view的時(shí)候,會(huì)有多少種寫法呢?一種是寬高都是warp_content,一種是寬高都是match_parent,一種是寬是warp_content高是match_parent,反之一樣,一種寬高都是指定值,一種是寬或者高給定值,另外一個(gè)是warp_content或者match_parent,其實(shí)這么多種之中,有幾種是重復(fù)利用的,就是當(dāng)寬高其中之一指定值,另外一個(gè)是match_parent,或者是兩者都是match_parent,前面說(shuō)了,這種都是相當(dāng)于給了明確值,都是EXACTLY模式,其實(shí)onMeasure方法主要的是針對(duì)AT_MOST做處理,那么這樣就好辦了
首先獲取到測(cè)量模式以及測(cè)量值
然后就是一系列的判斷了