View的layout

View的layout()

下面的方法解釋均翻譯於官方文檔
 /**
     * layout機制有兩個階段
     * 第一階段:測量,在這個階段,每個父view都會調用layout來爲子view指定位置,最經典的做法就是使用存於measure pass()的子view測量值
     * 第二階段:分配一個大小和位置給此view和它所有的子view
     * View的衍生類不應該重寫這個方法,若該類有子view應該重寫onLayout(),並在這個方法調用每個子view的layout()
     * 上下左右的位置都是相對於該view的父控件來說的。
     */
    public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
    /** 
     * onMesure()
     * 測量視圖和它的內容來確定寬高,注意這個方法是由measure()喚醒,並且在子類重寫來爲它的內容提供更精準有效的測量值。
     * 基類測量的實現默認爲背景的大小,除非MeasureSpec提供更大的size。子類應該重寫onMeasure()爲自己的內容提供更好的測量值
     * 規定:當年重寫這個方法時必須調用setMeasuredDimension()來保存這個view的寬高值。若是沒保存成功就會觸發measure()拋出IllegalStateException異常。調用父類的onMeasure()是個比較好的辦法。
     * 如果這個方法在子類被重寫,那它應該確保測量的寬高至少是視圖的最低寬高。
     * @param widthMeasureSpec 
     * @param heightMeasureSpec 
     * 兩個參數代表這個控件父view的寬高,並與MeasureSpec有緊密聯繫
     */
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        }
    /**
     * setFrame()
     * 爲這個view分配大小和位置。此方法在layout()裏調用
     * 上下左右的位置都是相對於父view的
     * 大小與位置與前一次有改變就會返回true
     */
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
    /**
     * onLayout()
     * 當這個view需要爲子view分配大小和位置時會在layout()裏調用它。
     * 所以有子view的衍生類應該重寫這個方法並調用它的每個子view的layout()。
     * 5個參數的解釋跟setFrame()一樣。
     */
            onLayout(changed, l, t, r, b);
        }
}

總結:大致的layout過程是先調用setFrame()爲這個view分配大小和位置,然後回調onLayout函數,對於View來說onLayout只是一個空實現,一般只有當它是ViewGroup需要爲子view分配大小和位置時才重寫它。然後它會參考measure過程中計算得到的mMeasuredWidth和mMeasuredHeight來安排子視圖在父視圖中顯示的位置,但這不是必須的,我們可以自己傳入4個參數調用layout()來安排子view具體位置。
其實具體measure()是什麼時候執行的還是不清楚,找了好久也沒找着,不過目前來說這個不重要就先放放吧。

MeasureSpec

上面說onMeasure(widthMeasureSpec,heightMeasureSpec)時就說到這兩個參數跟MeasureSpec有密切聯繫,我們先就大致知道怎麼用他們就行。
1. MeasureSpec.getMode(widthMeasureSpec)有三個值
- EXACTLY:測量的佈局大小是精確的,比如match_parent或具體dp值。
- AT_MOST:子佈局可以根據自己的大小選擇任意大小。比如wrap_content,但它最大不能超出父控件大小 。
- UNSPECIFIED:父佈局沒有給子佈局任何限制,子佈局可以任意大小。這種情況大多出現與ListView 。
2. MeasureSpec.getSize(widthMeasureSpec)
得到測量大小,以像素爲單位。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章