Android自定義控件常用方法總結(jié)
inflate方法常常用來(lái)解析一個(gè)xml布局文件,在自定義組合式控件中常常使用,使用的姿勢(shì)包括:
View.inflate(context, resource, root)LayoutInflater.from(getContext()).inflate(resource, root);
而View.inflate其實(shí)還是調(diào)用的LayoutInflater去解析一個(gè)xml:
public static View inflate(Context context, int resource, ViewGroup root) {LayoutInflater factory = LayoutInflater.from(context);return factory.inflate(resource, root); }
所以這兩種姿勢(shì)沒(méi)啥區(qū)別,這里來(lái)討論一下inflate(resouce, root)的返回值,參數(shù)resource是布局資源,root是傳入的一個(gè)根節(jié)點(diǎn)。如果root傳入一個(gè)null,inflate就會(huì)解析resource對(duì)應(yīng)的xml,返回這個(gè)xml中的根節(jié)點(diǎn),如果root傳入不為null,inflate會(huì)解析這個(gè)xml布局并且添加到根節(jié)點(diǎn)root下,然后返回根節(jié)點(diǎn)root。
另外還有帶三個(gè)參數(shù)的inflate方法:
inflate(int resource, ViewGroup root, boolean attachToRoot)
這里多了一個(gè)參數(shù)attachToRoot,如果root為null,則返回解析后的xml布局中的根節(jié)點(diǎn);如果root不為null,attachToRoot為true,inflate會(huì)解析這個(gè)xml布局并且添加到根節(jié)點(diǎn)root下,然后返回根節(jié)點(diǎn)root;如果root不為null,attachToRoot為false,inflate會(huì)解析這個(gè)xml布局但不會(huì)添加到根節(jié)點(diǎn)root下,然后返回解析后的xml布局中的根節(jié)點(diǎn),這時(shí)候root的作用只是為xml中的根節(jié)點(diǎn)提供布局參數(shù)的屬性,因?yàn)閤ml中的根節(jié)點(diǎn)不知道自己的父容器是誰(shuí),所以如果沒(méi)有人給它提供的話,它的布局參數(shù)就會(huì)失效。
onFinishInflateonFinishInflate是當(dāng)所有的孩子都解析完后的一個(gè)調(diào)用。比如我們自定義一個(gè)ViewGroup,想要去找到孩子做一些設(shè)置,這時(shí)候如果在自定義ViewGroup的構(gòu)造函數(shù)去findViewById的話,會(huì)返回一個(gè)null,因?yàn)榇藭r(shí)孩子還沒(méi)有解析好,也就是還沒(méi)有生出來(lái)。這時(shí)候我們可以去覆寫onFinishInflate,當(dāng)孩子解析好后再去find。
requestLayout關(guān)于requestLayout的介紹比較多,requestLayout()方法會(huì)觸發(fā)measure過(guò)程和layout過(guò)程,不會(huì)調(diào)用draw過(guò)程,也不會(huì)重新繪制任何View包括該調(diào)用者本身
onSizeChange(int w, int h, int oldw, int oldh)onSizeChange是控件的大小發(fā)生變化的時(shí)候的調(diào)用,它的調(diào)用軌跡是layout->setFrame->sizeChange->onSizeChange。當(dāng)控件第一次布局時(shí)肯定會(huì)被調(diào)用到,我們覆寫該方法可以獲取到控件的大小。所以這個(gè)方法通常被用來(lái)在里面初始化跟控件大小相關(guān)的成員變量。
invalidateinvalidate使用的非常頻繁,它會(huì)觸發(fā)View的重新繪制,也就是繪制流程的draw過(guò)程,但不會(huì)調(diào)用測(cè)量和布局過(guò)程
postInvalidate我們都知道Android的UI是單線程模型,只能在主線程更新UI,所以我們只能在主線程調(diào)用invalidate,如果想要在子線程更新ui,可以使用handler發(fā)送一個(gè)msg到主線程,然后在處理msg的時(shí)候去調(diào)用invalidate。另外,我們可以直接調(diào)用postInvalidate去在子線程更新UI,postInvalidate內(nèi)部實(shí)現(xiàn)也是使用handler來(lái)做發(fā)送msg到主線程然后調(diào)用invalidate。
setWillNotDraw自定義ViewGroup通常是不會(huì)去繪制自己的,如果大家重寫ViewGroup里面的draw方法或者onDraw方法會(huì)發(fā)現(xiàn)它們根本就不會(huì)被調(diào)用到。但是如果給你的ViewGroup設(shè)置一個(gè)背景,就會(huì)發(fā)現(xiàn)draw方法和onDraw方法又都會(huì)走了。
我們知道ViewGroup本身是一個(gè)View,它的繪制是被其父容器發(fā)起的,具體的位置是在ViewGroup中的drawChild方法:
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {return child.draw(canvas, this, drawingTime); }
注意這里的draw方法是帶三個(gè)參數(shù)的,與我們通常講的帶一個(gè)參數(shù)的draw方法不一樣。在View類中找到帶三個(gè)參數(shù)的draw方法,發(fā)現(xiàn)里面有這么一段代碼:
if (!hasDisplayList) { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {mPrivateFlags &= ~PFLAG_DIRTY_MASK;dispatchDraw(canvas); } else {draw(canvas); }}
從這里我們可以看出一點(diǎn)端倪,通常一個(gè)ViewGroup默認(rèn)是會(huì)跳過(guò)繪制的,也即 (mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW 會(huì)返回一個(gè)true,那么會(huì)直接走dispatchDraw方法去畫它自己的孩子去了,并不會(huì)調(diào)用帶一個(gè)參數(shù)的 draw(canvas) ,但是當(dāng)這個(gè)ViewGroup有背景或者setWillNotDraw(false)是,就會(huì)走 draw(canvas) 方法。
所以如果我們自定義一個(gè)ViewGroup并且想要實(shí)現(xiàn)它本身的繪制的話,就可以給它設(shè)置一個(gè)背景或者調(diào)用 setWillNotDraw(false)
onAttachedToWindowonAttachedToWindow是當(dāng)一個(gè)View綁定到window上時(shí)的調(diào)用,根據(jù)View類里面的對(duì)這個(gè)方法的注釋,onAttachedToWindow肯定會(huì)在onDraw方法之前調(diào)用。
在自定義控件里面,我們可以在onAttachedToWindow注冊(cè)一些廣播接收器,觀察者或者開(kāi)啟一些任務(wù),大家可以參考TextClock的里面的實(shí)現(xiàn)。
onDetachedFromWindowonDetachedFromWindow對(duì)應(yīng)于onAttachedToWindow,是當(dāng)一個(gè)View從window上移除時(shí)的一個(gè)調(diào)用。如果在onAttachedWindow里面注冊(cè)了一些監(jiān)聽(tīng),那么通常就要在onDetachedFromWindow里面反注冊(cè)。
ViewTreeObserverViewTreeObserver是視圖樹的觀察者,監(jiān)聽(tīng)一些視圖樹的全局變化,這些全局變化包括整個(gè)視圖樹的布局,開(kāi)始繪制,觸摸模式的變化等。我們不能直接初始化ViewTreeObserver的對(duì)象,需要通過(guò)getViewTreeObserver()去獲取。
ViewTreeObserver.OnGlobalLayoutListener當(dāng)在一個(gè)視圖樹中全局布局發(fā)生改變或者視圖樹中的某個(gè)視圖的可視狀態(tài)發(fā)生改變的監(jiān)聽(tīng)器,一般的使用姿勢(shì)是:
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() {getViewTreeObserver().removeGlobalOnLayoutListener(this);//do something you like//for example, get view width or height height }});ViewTreeObserver.OnPreDrawListener
當(dāng)一個(gè)視圖樹將要繪制時(shí)的監(jiān)聽(tīng)器,一般的使用姿勢(shì)是:
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() {//do something before draw//for example, request a new layoutreturn true; }});
來(lái)自:http://www.jianshu.com/p/744550c02cf1
相關(guān)文章:
1. python3實(shí)現(xiàn)往mysql中插入datetime類型的數(shù)據(jù)2. python爬蟲實(shí)戰(zhàn)之制作屬于自己的一個(gè)IP代理模塊3. moment轉(zhuǎn)化時(shí)間戳出現(xiàn)Invalid Date的問(wèn)題及解決4. python如何實(shí)現(xiàn)word批量轉(zhuǎn)HTML5. PHP如何打印跟蹤調(diào)試信息6. Java8內(nèi)存模型PermGen Metaspace實(shí)例解析7. 存儲(chǔ)于xml中需要的HTML轉(zhuǎn)義代碼8. HTML <!DOCTYPE> 標(biāo)簽9. CSS3中Transition屬性詳解以及示例分享10. 匹配模式 - XSL教程 - 4
