天天看點

Android -- View

setContentView                                                                       

隻要你使用過Activity,那麼你一定使用過setContentView這個方法。一般都是這樣調用該方法:

然後,在手機或者模拟器上就可以看見自己的布局。

如果,你留意的話,setContentView還有很多過載方法:

Android -- View
Android -- View

那麼,getWindow()方法是做什麼的呢?一探究竟:

可以看出,該方法傳回一個Window執行個體。但是Window是一個抽象類啊,怎麼可以有執行個體對象???原來,Window類有一個子類PhoneWindow,那麼如何得知getWindow傳回的是PhoneWindow執行個體呢?來,看下面這張圖:

Android -- View

至此,您應該明白setContentView()方法是調用PhoneWindow類的同名方法。源碼如下:

Android -- View
Android -- View

每個Activity都會執行個體化一個Window并且隻有一個,而View就像是貼在Window上的裝飾品。窗戶(Window)隻有一個,但是窗花(View)可以有很多。

LayoutInflater                                                                        

獲得 LayoutInflater 執行個體

Android -- View
Android -- View

對于第一種,主要是調用 Activity 的 getLayoutInflater() 方法。

繼續跟蹤研究 android 源碼,Activity 中的該方法是調用 PhoneWindow 的 getLayoutInflater()方法!

可以看出它其實是調用 LayoutInflater.from(context), 那麼該方法其實是調用第二種方法,看看源碼,如下:

Android -- View
Android -- View

inflate 方法

inflate 原意是充氣之類的,在這裡主要意思就是,擴張、使之膨脹。換句話說就是将目前視圖view補充完整、擴充該視圖。

Android -- View
Android -- View

示例代碼:

Android -- View
Android -- View

對于上面代碼,指定了第二個參數 ViewGroup root,當然你也可以設定為 null 值。

注意:該方法與 findViewById 方法不同。

inflater 是用來找 layout 下 xml 布局檔案,并且執行個體化!而 findViewById() 是找具體 xml 下的具體 widget 控件(如: Button,TextView 等)。

postInvalidate()   (參考)                                                      

在子線程中控制UI:

Android -- View
Android -- View

postInvalidate() 方法,源碼:

Android -- View
Android -- View

其實,是調用了 Handler 的處理消息的機制!該方法可以在子線程中直接用來更新UI。但是在 Button 的事件中開啟線程,更新 UI就會報錯報異常。

Handler 和 invalidate 方法結合多線程更新 UI                              

方法 invalidate 主要用在主線程中(即UI 線程中),不可以用于子線程。如果在子線程中需要使用 postInvalidate 方法。

Android -- View
Android -- View

invalidate 方法如果你直接在主線程中調用,是看不到任何更新的。需要與Handler結合!

Android 在 onDraw 事件處理繪圖,而 invalidate() 函數可以再一次觸發 onDraw 事件,然後再一次進行繪圖動作。

Android -- View
Android -- View

經過測試,發現 times 一直在++,說明 onDraw 被多次調用,并且一直在畫圖!

注釋掉的話:

Android -- View

可以看出,圖像隻畫了一條線,說明onDraw()方法被調用一次。從log上也可以看出來:

那麼,是什麼力量促使onDraw()方法被調用呢?

setContentView()View view方法,其實是調用PhoneWindow的setContentView(View view)方法,調用關系如下:

Android -- View

進而可以看出,invalidate()方法是促使onDraw()方法被調用的力量。

那麼,修改代碼,将内部類MyView的onDraw()方法中的invalidate()注釋取消,再看看運作效果:

Android -- View
Android -- View

控制台:

Android -- View
Android -- View

可以看出,invalidate()方法使onDraw()一直被調用,實作重繪的效果。

在invalidate()方法源碼中,有這麼一段注釋:

這段話,說明了上面的實作(調用onDraw()方法)。但是在子線程中必須使用postInvalidate()方法。

invalidate()源碼分析                                                                

Android -- View
Android -- View

子沒有多大的變化,隻是在onCreate()方法中直接調用invalidate()方法,如:

這樣做的目的主要是想看看,自己調用View的invalidate()方法會不會觸發onDraw()方法。運作一下:

Android -- View

nDraw()方法并沒有執行!那麼是不是因為沒有調用setContentVIew()方法呢?修改onCreate()方法:

Android -- View
Android -- View

再次運作,效果:

Android -- View

說明,隻有setContentVIew()方法中的invalidate()方法啟了作用,自己調用View的invalidate()方法,mView.invalidate()沒啟任何作用。但是,在MyView的onDraw()方法中調用invalidate()方法可以循環調用onDraw()方法,類似遞歸。

分析一下,invalidate()方法的源碼吧,在這裡也許可以找到答案。

Android -- View
Android -- View

這裡可以看到p.invalidateChild(this, r)(看源碼隻看關鍵部分,不然你會很暈!),其中p是ViewParent執行個體對象。ViewParent是一個接口,現在我們關心誰實作了這個接口?

通過千辛萬苦的search,終于找到ViewParent的實作類ViewRoot:

Android -- View
Android -- View

那麼,看看該類實作的invalidateChild()方法:

Android -- View
Android -- View

關鍵代碼在這兒:

這個方法是向Handler發送消息:

Android -- View
Android -- View

接下來,看看ViewRoot的Handler的handleMessage的實作:

Android -- View
Android -- View

performTraversals()方法,調用ViewRoot的私有方法private void draw(boolean fullRedrawNeeded),在該方法中有句代碼很關鍵:

其實這句代碼,就是調用View的draw()方法 ,關鍵代碼:

也就是說,滿足這個方法,就會回調onDraw()方法。到此為止,您應該明白,當我們自己調用invalidate()方法時,想使onDraw()方法回調,必須滿足條件。

調用關系,請看草圖!

Android -- View
Android -- View

我是天王蓋地虎的分割線                                    

<b></b>

<b>本文轉自我愛物聯網部落格園部落格,原文連結:http://www.cnblogs.com/yydcdut/p/3851955.html,如需轉載請自行聯系原作者</b>

繼續閱讀