日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android layout过程分析,Andriod 从 0 开始自定义控件之 View 的 layout 过程 (八)

發布時間:2025/3/12 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android layout过程分析,Andriod 从 0 开始自定义控件之 View 的 layout 过程 (八) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在上一篇文章了,我們學習了 View 三大流程之一的 measure 過程,當 measure 過程完成后,View 的大小就測量好了。接下來就到了 layout 的過程了,layout 的過程就是用于確定 View 的位置。下面通過查看源碼,來更深入的了解下 layout 的整個過程。

源碼分析

View 的 layout 過程是從 ViewRoot 開始的,ViewRoot 的 performTraversals 方法會在 measure 過程執行完后繼續執行 performLayout 方法,在該方法中又執行了 View 的 layout 方法:

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());復制代碼

該方法接收四個參數,分別是 left、top、right、bottom 四個坐標,代表 View 的四個頂點位置,其中 left、top 的參數為 0,right、bottom 則把測量好的寬、高傳入了進來,下面繼續看下 layout 方法的具體實現:

public void layout(int l, int t, int r, int b) {

if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {

onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);

mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

}

// 記錄 View 的原始位置

int oldL = mLeft;

int oldT = mTop;

int oldB = mBottom;

int oldR = mRight;

// 判斷 isLayoutModeOptical 方法的返回值, true 則執行 setOpticalFrame 方法,否則執行 setFrame

// setOpticalFrame 方法最終也走了 setFrame 方法,所以最終都會執行 setFrame 方法

// setFrame 方法會判斷 View 位置是否發生改變, 如果發生改變, 會將 View 新的 left、top、right、bottom 值賦值給成員變量,并返回一個 boolean 值,表示位置是否改變

// 當 setFrame 完成后,表示 View 本身的位置已經確定

boolean changed = isLayoutModeOptical(mParent) ?

setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

// 如果位置發生改變,執行 onLayout 方法,該方法在 View 中是個空實現,需要繼承 ViewGroup 的類實現

// onLayout 方法的作用是 ViewGroup 確定子 View 的位置

if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {

onLayout(changed, l, t, r, b);

mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnLayoutChangeListeners != null) {

ArrayList listenersCopy =

(ArrayList)li.mOnLayoutChangeListeners.clone();

int numListeners = listenersCopy.size();

for (int i = 0; i < numListeners; ++i) {

listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);

}

}

}

mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;

mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;

}復制代碼

layout 方法首先通過調用 setFrame 方法判斷 View 的位置是否發生改變,如果發生改變,則將新位置的值 left、top、right、bottom 賦值給 mLeft、mTop、mRight、mBottom 這幾個成員變量,這四個值保存著 View 的位置信息,我們可以通過 getLeft、getTop、getRight、getBottom 方法獲取到。

所以我們如果想要得到 View 的位置信息,那么必須在 setFrame 方法執行完畢后獲取,比如說在 onLayout 方法中獲取,因為通過看源碼我們已經知道,onLayout 方法是在 setFrame 方法之后執行的。

當 setFrame 方法執行完成后,會返回一個 View 位置是否發生改變的 boolean 值,如果發生改變,那么就會走 onLayout 方法,該方法在 ViewGroup 中調用,用于確定子 View 的位置。

由于具體每種布局的實現效果都不同,所以 onLayout 的默認實現,和上一篇 measure 測量過程中 ViewGroup 的 onMeasure 方法一樣,是空實現,具體怎么確定子 View 的位置,由 ViewGroup 的具體實現類去作不同實現。

下面,我們可以自定義一個只能包含一個 View 的布局,來實現下 onLayout 方法,有興趣的同學也可以自己去看看 LinearLayout 等布局的 onLayout 實現。

實例 (單布局)

代碼實現:

public class SingleLayout extends ViewGroup {

public SingleLayout(Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if(getChildCount() > 0){

View childAt = getChildAt(0);

measureChild(childAt, widthMeasureSpec, heightMeasureSpec);

}

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

if(getChildCount() > 0){

View childAt = getChildAt(0);

childAt.layout(0, 0, childAt.getMeasuredWidth(), childAt.getMeasuredHeight());

}

}

}復制代碼

該自定義布局很簡單,首先在 onMeasure 方法中判斷是否有子 View,如果有,那么只測量第一個 View。接著在 onLayout 中判斷是否有子 View,如果有,則調用了第一個子 View 的 layout 方法,分別傳入 0, 0, childAt.getMeasuredWidth(), childAt.getMeasuredHeight() 四個參數,這四個參數分別代表了子 View 在 當前自定義布局中的位置,也就是放置在當前自定義布局的左上角。

當子 VIew 在 layout 方法中確定了自己的位置后,如果子 View 是 ViewGroup 那么又會去調用 onLayout 方法去確定它子 View 的位置。這樣一層一層傳遞下去,就完成了整個 View 數的 layout 過程。

下面我們把剛剛寫的布局運行下,看下效果:

android:layout_width="match_parent"

android:layout_height="wrap_content">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World!"/>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World2!"/>

復制代碼

運行結果:

可以看到,我們盡管在布局下面放了兩個 View,但最終顯示的只有一個 View。

getMeasuredWidth 和 getWidth 的區別

getMeasuredWidth / getMeasuredHeight 的值是在 onMeausre 方法結束后可以獲取到的,getWidth / getHeight 的值是在 onLayout 方法結束后可以獲取到的。

通過查看 View 的 getWidth 、getHeight 源碼:

public final int getWidth() {

return mRight - mLeft;

}

public final int getHeight() {

return mBottom - mTop;

}復制代碼

結合 mLeft、mTop、mRight、mBottom 這四個成員變量的賦值過程來看,getWidth 方法的返回值剛好就是 getMeasuredWidth,因為 getMeasuredWidth - 0 不就還是 getMeasuredWidth 嗎。

但是在某些特殊情況下,還是會導致兩者的返回值不相同,比如說以剛剛自定義的 SingleLayout 的例子看來,我如果將調用子 View 的 layout 方法修改為:

childAt.layout(100, 100, childAt.getMeasuredWidth(), childAt.getMeasuredHeight());復制代碼

那么最終獲取的結果,會發現 getWidth 的值比 getMeasuredWidth 的值少 100px。雖然這樣做沒有啥意義,但是證明了測量寬高并不一定會等于最終的寬高。

還有一種情況,就是當 View 需要多次測量才能確定自己的測量寬高時,那么在前幾次的測量過程中得出的測量寬高有可能并不等于最終的測量寬高,這時獲取的測量寬高并不一定與最終寬高相等。

參考

總結

以上是生活随笔為你收集整理的android layout过程分析,Andriod 从 0 开始自定义控件之 View 的 layout 过程 (八)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。