編輯:關於Android編程
為了研究Android中View的布局及繪圖機制,我創建了一個非常簡單的App,該App只有一個Activity,該Activity對應的layout如下所示:
該布局文件很簡單,RelativeLayout下面就一個TextView。
我們啟動App後,通過Hierarchy Viewer查看App中的布局層級,如下所示:
從上圖我們可以看出,App的根結點是 關於Measure: View用measure()方法進行量算,量算的目的是View讓其父節點知道它想要多大的尺寸,所以說量算是後面對View進行布局以及繪圖的基礎。 View的measure()方法中會執行onMeasure()方法,View類本身的onMeasure()方法不是空方法,其將量算完的結果保存到View中。View的子類不應該重寫measure()方法,如果需要的話應該重寫onMeasure()方法,ViewGroup的子類都應該重寫onMeasure()方法,比如 量算的起點是ViewRootImpl類,ViewRootImpl是根View,即View樹上面的根結點,嚴格來說ViewRootImpl不屬於View,其實現了ViewParent接口, 其下才是 Android在對View樹進行自上而下的量算時,采用的是深度優先算法,而非廣度優先算法,即遍歷到某個View時,Android會首先沿著該View一直縱向遍歷並量算到處於葉子節點的View,只有對該View及其所有子孫View(如果存在子孫View的話)完成量算後,才會量算該View的兄弟節點View。 以下是Android對所有View自上而下量算的調用過程: 由上我們可以看出,首先ViewRootImpl執行了doTraversal()和performTraversals() 方法,然後執行ViewRootImpl的performMeasure()方法,該方法是Android對所有View進行量算的起點。在該方法中會從ViewRootImpl開始自上而上對View樹進行遍歷,首先ViewRootImpl對 LinearLayout在measure()方法中會調用onMeasure()方法,在該方法中LinearLayout調用了measureVertical()方法,該方法會遍歷其child並對其進行量算,由於其子節點ViewStub不用於渲染,所以此處不對其量算,對其忽略,對另一個child FrameLayout進行量算,調用FrameLayout的measure()方法。 FrameLayout在執行measure()方法時會執行onMeasure()方法,在該方法中會遍歷所有的child,並對它們進行量算。其下只有一個child,即RelativeLayout,調用RelativeLayout的measure()方法,對其進行量算。 RelativeLayout在measure()方法中會執行onMeasure()方法,在該方法中會遍歷所有的child,並對它們進行量算。其下只有一個child,即TextView,調用TextView的measure()方法對其進行量算,在其中會執行onMeasure()方法。 以上完成了對View樹中LinearLayout及其所有子算View的量算工作,之後會對 這樣整個View樹自上而下的量算過程就結束了,經過量算Android知道了各個View想要渲染的尺寸大小,即寬度和高度信息。 關於量算中measure()和onMeasure()方法的一些細節會另外寫博文介紹。 關於Layout: 布局的前提是已經對View進行了量算,View通過調用layout()方法進行布局,布局的目的是讓Android知道View在其父控件中的位置,即距父控件四邊的距離left、right、top、bottom。布局是繪圖的基礎,只有完成了布局,才能對View進行繪圖。 View的layout()方法中會執行onLayout()方法,View類本身的onLayout()是空方法。View的子類不應該重寫layout()方法,如果需要的話應該重寫其onLayout()方法,ViewGroup的子類都應該重寫onLayout()方法,比如PhoneWindow$DecorView、RelativeLayout、FrameLayout、RelativeLayout都重寫了onLayout()方法,這些類都在onLayout()方法中遍歷child,並調用child的layout()方法,對child進行布局,縱向遞歸進行,從而實現自上而下對View樹進行布局,直至完成對葉子節點View的布局。 布局的起點也是ViewRootImpl類,ViewRootImpl是根View,即View樹上面的根結點,嚴格來說ViewRootImpl不屬於View,其實現了ViewParent接口, 其下才是 Android在對View樹進行自上而下的布局時,采用的是深度優先算法,而非廣度優先算法,即遍歷到某個View時,Android會首先沿著該View一直縱向遍歷並布局到處於葉子節點的View,只有對該View及其所有子孫View(如果存在子孫View的話)完成布局後,才會布局該View的兄弟節點View。 Android中的布局過程與之前上面提到的量算過程很類似,以下是Android對所有View自上而下布局的調用過程: 由上我們可以看出,首先ViewRootImpl執行了doTraversal()和performTraversals() 方法,然後執行ViewRootImpl的performLayout()方法,該方法是Android對所有View進行布局的起點。在該方法中會從ViewRootImpl開始自上而下對View樹進行遍歷,首先ViewRootImpl執行 LinearLayout在layout()方法中會執行onLayout()方法,在該方法中會調用layoutVertical()方法,該方法會遍歷其所有的child並依次調用child的layout()方法進行布局。由於其子節點ViewStub不用於渲染,所以此處不對其進行布局,對其忽略,對另一個child FrameLayout進行布局,調用FrameLayout的layout()方法。 FrameLayout在layout()方法中會執行onLayout()方法,在該方法中會調用layoutChildren()方法,該方法會遍歷其所有的child並依次調用child的layout()方法進行布局。其下只有一個child,即RelativeLayout,執行RelativeLayout的layout()方法,對其進行布局。 RelativeLayout在layout()方法中會執行onLayout()方法,在該方法中會遍歷所有的child並依次調用child的layout()方法進行布局。其下只有一個child,即TextView,調用TextView的layout()方法對其進行布局,在其中會執行onLayout()方法。 以上完成了對View樹中LinearLayout及其所有子孫View的布局工作,之後會對 這樣整個View樹自上而下的布局過程就結束了,經過布局Android知道了各個View在其父控件中的位置。 關於Draw: 繪圖的前提是已經對View進行了量算和布局,View通過調用draw()方法進行繪圖,繪圖的目的就是讓View在UI界面上呈現出來。 View的draw()方法中會依次onDraw()和dispatchDraw()方法,View類本身的onDraw()和dispatchDraw()方法都是空方法。View的子類不應該重寫draw()方法,如果需要的話應該按具體情況選擇重寫onDraw()方法或dispatchDraw()方法,具體來說: 繪圖的起點也是ViewRootImpl類,ViewRootImpl是根View,即View樹上面的根結點,嚴格來說ViewRootImpl不屬於View,其實現了ViewParent接口, 其下才是PhoneWindow$DecorView。 Android在對View樹進行自上而下的繪圖時,采用的也是深度優先算法,而非廣度優先算法,即遍歷到某個View時,Android會首先沿著該View一直縱向遍歷並繪圖到處於葉子節點的View,只有對該View及其所有子孫View(如果存在子孫View的話)完成繪圖後,才會渲染該View的兄弟節點View。 Android中的繪圖過程與之前上面提到的量算、布局過程類似,以下是Android對所有View進行自上而下繪圖的調用過程: 由上我們可以看出,首先ViewRootImpl執行了doTraversal()和performTraversals() 方法,然後執行ViewRootImpl的performDraw()方法,該方法是Android對所有View進行繪圖的起點。在該方法中會從ViewRootImpl開始自上而下對View樹進行遍歷,首先ViewRootImpl執行 LinearLayout在darw()方法中也會依次執行onDraw()方法和dispatchDraw()方法,在dispatchDraw()方法中會遍歷所有的child,調用child的draw()方法,對child進行繪圖。由於其子節點ViewStub不用於渲染,所以此處不對其進行繪圖,對其忽略,對另一個child FrameLayout進行繪圖,調用FrameLayout的draw()方法。 FrameLayout在draw()方法中也會依次執行onDraw()方法和dispatchDraw()方法,在dispatchDraw()方法中會遍歷所有的child,調用child的draw()方法,對child進行繪圖。其下只有一個child,即RelativeLayout,執行RelativeLayout的draw()方法,對其進行繪圖。 RelativeLayout在draw()方法中也會依次執行onDraw()方法和dispatchDraw()方法,在dispatchDraw()方法中會遍歷所有的child,調用child的draw()方法,對child進行繪圖。其下只有一個child,即TextView,執行TextView的draw()方法,對其進行繪圖,並在其中執行TextView的onDraw()方法,對TextView進行實際的渲染。 以上完成了對View樹中LinearLayout及其所有子孫View的繪圖工作,之後會對 當我們在onCreate()方法中調用 量算、布局、繪圖的起點都是ViewRootImpl 通過調用ViewRootImpl的performMeasure() 方法,開始驅動Android自上而下對所有View進行量算,這樣Android就知道了每個View想要的尺寸大小,即寬高信息 在完成了對所有View的量算工作後,通過調用ViewRootImpl的performLayout()方法,開始驅動Android會自上而下對所有View進行布局,Android就知道了每個View在其父控件中的位置,即View到其父控件四邊的left、right、top、bottom 在完成了對所有View的布局工作後,通過調用ViewRootImpl的performDraw()方法,開始驅動Android會自上而下對所有View進行繪圖,這樣Android就將所有的View渲染到屏幕上了 希望本文對大家理解Android中View的布局和繪圖機制有所幫助。 PhoneWindow$DecZ喎?/kf/ware/vc/" target="_blank" class="keylink">vclZpZXc8L2NvZGU+o6y0y7SmtcQkse3KvkRlY29yVmlld8rHUGhvbmVXaW5kb3fPwsPmtcTE2rK/wODKtcD9oaM8Y29kZT5QaG9uZVdpbmRvdyREZWNvclZpZXc8L2NvZGU+z8LD5tPQyP249mNoaWxko6y31rHwysdMaW5lYXJMYXlvdXTKtcD9oaJWaWV3QDQ5ZGEwNDO6zVZpZXdANDRmZjQxMKGjVmlld0A0OWRhMDQzse3KvrXEysduYXZpZ2F0aW9uQmFyQmFja2dyb3VuZKOsVmlld0A0NGZmNDEwse3KvrXEysdzdGF0dXNCYXJCYWNrZ3JvdW5koaNMaW5lYXJMYXlvdXTPwsPm09DBvbj2Y2hpbGSjrLfWsfDKx1ZpZXdTdHViyrXA/brNRnJhbWVMYXlvdXTKtcD9o6zG5NbQVmlld1N0dWKyu9Do0qq75tbGo6zL+dLUztLDx9Taz8LD5rXEzNbC29bQv8nS1NaxvdO21MbkuvbC1KGjRnJhbWVMYXlvdXTPwtPQ0ru49mNoaWxko6xSZWxhdGl2ZUxheW91dMq1wP2jrLjDUmVsYXRpdmVMYXlvdXTKtcD9ttTTprXEvs3Kx7K8vtbOxLz+YWN0aXZpdHlfbWFpbi54bWzW0LXEUmVsYXRpdmVMYXlvdXSjrFJlbGF0aXZlTGF5b3V0z8LT0NK7uPZjaGlsZKOsvLRUZXh0Vmlld6GjPC9wPg0KPHA+0tTJz8zhtb21xL/YvP62vMrHVmlld7XEyrXA/aOs09C1xNTyysdWaWV3R3JvdXC1xMq1wP2jrFZpZXdHcm91cLzMs9DX1FZpZXejrDxjb2RlPlBob25lV2luZG93JERlY29yVmlldzwvY29kZT6holJlbGF0aXZlTGF5b3V0oaJGcmFtZUxheW91dKGiUmVsYXRpdmVMYXlvdXS2vNaxvdO78rzkvdO8zLPQ19RWaWV3R3JvdXCjrNa709BWaWV3R3JvdXDKtcD9ssXE3NPQ19O92rXjoaM8L3A+DQo8cD61sc7Sw8fU2m9uQ3JlYXRlKCm3vbeo1tC199PDPGNvZGU+c2V0Q29udGVudFZpZXcoUi5sYXlvdXQuYWN0aXZpdHlfbWFpbik8L2NvZGU+t723qLrzo6xBbmRyb2lku+G002xheW91dLXEyvfQzr3hubnW0NfUyc+2+M/Cv6rKvLbUy/nT0LXEVmlld7340NDBv8vjoaKyvL7WoaK75s28o6y+38zlwLTLtb6tuf3S1M/Cuf2zzKO6PC9wPg0KPHA+QW5kcm9pZNfUyc+2+M/CttTL+dPQVmlld7340NDBv8vjo6zV4tH5QW5kcm9pZL7N1qq1wMHLw7+49lZpZXfP69KqtcSz37TntPPQoaOsvLS/7bjf0MXPojwvcD4NCjxwPtTazeqzycHLttTL+dPQVmlld7XEwb/L47mk1/e686OsQW5kcm9pZLvh19TJz7b4z8K21Mv509BWaWV3vfjQ0LK8vtajrEFuZHJvaWS+zdaqtcDBy8O/uPZWaWV31NrG5Li4v9i8/tbQtcTOu9bDo6y8tFZpZXe1vcbkuLi/2Lz+y8Sx37XEbGVmdKGicmlnaHShonRvcKGiYm90dG9tPC9wPg0KPHA+1NrN6rPJwcu21Mv509BWaWV3tcSyvL7WuaTX97rzo6xBbmRyb2lku+HX1MnPtvjPwrbUy/nT0FZpZXe9+NDQu+bNvKOs1eLR+UFuZHJvaWS+zb2ry/nT0LXEVmlld+TWyL61vcbBxLvJz8HLPC9wPg0KPHA+0tTPwsrHyea8sLW9tcTP4LnYwOC1xNS0wuujujxiciAvPg0KVmlld9S0wus8YnIgLz4NClZpZXdHcm91cNS0wus8YnIgLz4NClZpZXdSb290SW1wbNS0wus8YnIgLz4NClBob25lV2luZG93JERlY29yVmlld9S0wus8YnIgLz4NCkxpbmVhckxheW91dNS0wus8YnIgLz4NCkZyYW1lTGF5b3V01LTC6zxiciAvPg0KUmVsYXRpdmVMYXlvdXTUtMLrPGJyIC8+DQpUZXh0Vmlld9S0wus8L3A+DQo8aHIgLz4NCjxoMiBpZD0="量算">量算
PhoneWindow$DecorView
、RelativeLayout、FrameLayout、RelativeLayout都重寫了onMeasure()方法,這些類都在onMeasure()方法中遍歷child,並調用child的measure()方法,對child進行量算,縱向遞歸進行,從而實現自上而下對View樹進行量算,直至完成對葉子節點View的量算。PhoneWindow$DecorView
。PhoneWindow$DecorView
進行量算,在執行到PhoneWindow$DecorView
的onMeasure()方法時,其遍歷所有的child,對依次它們進行量算,首先對調用LinearLayout的measure()方法,對第一個子節點LinearLayout進行量算。PhoneWindow$DecorView
中的另外兩個View進行量算,這也體現了Android采用深度優先算法對View樹進行遍歷量算的過程。View@49da0d3和View@44ff410會依次執行measure()方法和onMeasure()方法。
布局
PhoneWindow$DecorView
。PhoneWindow$DecorView
的layout()方法,對其進行布局。PhoneWindow$DecorView
在其layout()方法中會執行onLayout()方法,PhoneWindow$DecorView
會在onLayout()方法中遍歷其所有的child,並依次調用child的layout()方法,實現對child的布局。首先調用其第一個child LinearLayout的layout()方法。PhoneWindow$DecorView
中的另外兩個View進行布局,這也體現了Android采用深度優先算法對View樹進行遍歷布局的過程。View@49da043和View@44ff410會依次執行layout()方法和onLayout()方法。
繪圖
PhoneWindow$DecorView
的draw()方法,對其繪圖。PhoneWindow$DecorView
在其draw()方法中會依次執行onDraw()方法和dispatchDraw()方法,在dispatchDraw()方法中會遍歷所有的child,調用child的draw()方法,對child進行繪圖。首先調用其第一個child LinearLayout的draw()方法。PhoneWindow$DecorView
中的另外兩個View進行繪圖,這也體現了Android采用深度優先算法對View樹進行遍歷繪圖的過程。View@49da043和View@44ff410會依次執行draw()方法和onDraw()方法。
總結
setContentView(R.layout.activity_main)
方法後,Android會從layout的樹形結構中自上而下開始對所有的View進行量算、布局、繪圖:
現在項目裡面有一個需求,本項目裡面下載的視頻和文檔都不允許通過其他的播放器播放,在培訓機構裡面這樣的需求很多。防止有人交一份錢,把所有的課件就拷給了別人。這樣的事情培訓機
廢話少說,直接上圖,有圖有真相。這兩個工具欄全是用布局來實現的。底部工具欄布局代碼:代碼復制代碼 代碼如下: < xmlns:android
英文詞典是手機中經常使用的應用。因此,在本文將結合Android來討論如何實現一個Android版的英文詞典。實現英文詞典的方法很多。在本文使用了SQLite數據庫來保存
最近有很多朋友問我,android6.0下載文件怎樣設置文件的保存路徑。今天就把android6.0下載文件路徑設置總結一下。在android 6.0以前,你可以只關注外