熟悉繪制流程的都知道,ViewGroup
可以決定child的繪制時(shí)機(jī)以及調(diào)用次數(shù)。
今天我們就從RelativeLayout開始學(xué)起,看一下它對(duì)子View
的onMeasure
調(diào)用次數(shù)具體是多少。
簡(jiǎn)單起見,我們選擇進(jìn)入Activity
的時(shí)機(jī),在前面的blog進(jìn)入Activity時(shí),為何頁面布局內(nèi)View#onMeasure會(huì)被調(diào)用兩次?提到過,進(jìn)入頁面時(shí)最少會(huì)走兩遍繪制流程,我們需要觀測(cè)下每次繪制流程中,child的onMeasure
執(zhí)行次數(shù)。
系列文章:
從源碼角度理解FrameLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解LinearLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解RelativeLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解ConstraintLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
ViewGroup在調(diào)用onMeasure時(shí),會(huì)先測(cè)量父View,還是會(huì)先測(cè)量子View?
通過log觀測(cè)現(xiàn)象
時(shí)機(jī):進(jìn)入頁面;
xml布局:里面的自定義View都只是添加了log。
demo:RelativeLayoutTestActivity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".measure.RelativeLayoutTestActivity">
<com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.tinytongtong.androidstudy.measure.view.CustomSingleView
android:id="@+id/view1"
android:layout_width="200dp"
android:layout_height="100dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:background="@color/colorPrimaryDark" />
<com.tinytongtong.androidstudy.measure.view.CustomTextView
android:id="@+id/view2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99dddddd"
android:text="match_parent" />
<com.tinytongtong.androidstudy.measure.view.CustomButton
android:id="@+id/view3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/view5"
android:text="wrap_content" />
<com.tinytongtong.androidstudy.measure.view.CustomImageView
android:id="@+id/view4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/view5"
android:background="@drawable/ic_launcher"
android:contentDescription="wrap_content" />
<com.tinytongtong.androidstudy.measure.view.CustomHelloView
android:id="@+id/view5"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@color/background_debug" />
<com.tinytongtong.androidstudy.measure.view.CustomWorldView
android:id="@+id/view6"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/view5"
android:layout_centerHorizontal="true"
android:background="@color/background_info"
android:gravity="center" />
</com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout>
</LinearLayout>
這里給RelativeLayout添加了4個(gè)child,分別設(shè)置了不同的寬高。接著以RelativeLayout的寬高為變量,分別設(shè)置match_parent
和wrap_content
,我們觀察下對(duì)應(yīng)的onMeasure
執(zhí)行次數(shù)。
現(xiàn)實(shí)效果
寬高兩兩組合,一共有四種情況,具體效果如下表:
寬 | 高 | 自身 | view1(固定寬高,layout_alignParentRight,layout_alignParentBottom) | view2(w:match_parent,h:match_parent) | view3(w:match_parent,h:wrap_content,android:layout_alignBaseline="@id/view5") | view4(w:wrap_content,h:match_parent,android:layout_toLeftOf="@id/view5") | view5(固定寬高,android:layout_centerInParent="true") | view6(固定寬高,android:layout_below="@id/view5",android:layout_centerHorizontal="true") | 備注:第一次繪制處理mSortedHorizontalChildren,child的順序是6、5、4、3、2、1;第二次繪制處理mSortedVerticalChildren,順序是5、3、6、4、2、1; |
---|---|---|---|---|---|---|---|---|---|
match_parent | match_parent | M:2,L:1,D:0(默認(rèn)不參與onDraw) | M:4,L:1,D:1(參與第一、二次onMeasure) | M:4,L:1,D:1(參與第一、二次onMeasure) | M:4,L:1,D:2(參與第一、二次onMeasure,參與第一、二次onDraw) | M:4,L:1,D:1(參與第一、二次onMeasure) | M:4,L:1,D:1(參與第一、二次onMeasure) | M:4,L:1,D:1(參與第一、二次onMeasure) | |
match_parent | wrap_content | M:2,L:1,D:0 | 同上 | 同上 | 同上 | 同上 | 同上 | 同上 | |
wrap_content | match_parent | M:2,L:1,D:0 | 同上 | 同上 | 同上 | 同上 | 同上 | 同上 | |
wrap_content | wrap_content | M:2,L:1,D:0 | 同上 | 同上 | 同上 | 同上 | 同上 | 同上 |
說明:
M
:onMeasure
;L
:onLayout
;D
:onDraw
。
M:2,L:1,D:1
表示onMeasure
調(diào)用了2次,onLayout
調(diào)用了1次,onDraw
調(diào)用了一次。
我們知道,進(jìn)入Activity
時(shí),最少會(huì)走兩次onMeasure
方法,具體請(qǐng)看進(jìn)入Activity時(shí),為何頁面布局內(nèi)View#onMeasure會(huì)被調(diào)用兩次?。
觀察表格中的內(nèi)容,我們發(fā)現(xiàn)在一次測(cè)量流程中,RelativeLayout的child,最少是一次測(cè)量
,最多是兩次測(cè)量
,依賴關(guān)系
會(huì)對(duì)測(cè)量次數(shù)有影響。
RelativeLayout#onMeasure(int widthMeasureSpec, int heightMeasureSpec)源碼分析
具體是怎樣的情況呢?我們看下源碼:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 準(zhǔn)備數(shù)據(jù)。遍歷所有child,將不包含水平依賴的child添加進(jìn)mSortedHorizontalChildren;
// 將不包含垂直依賴的child添加進(jìn)mSortedVerticalChildren。
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
sortChildren();
}
...
// 第一次測(cè)量,測(cè)量所有不包含水平依賴關(guān)系的child
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
...
measureChildHorizontal(child, params, myWidth, myHeight);
...
}
}
// 第二次測(cè)量,測(cè)量所有不包含垂直依賴關(guān)系的child
views = mSortedVerticalChildren;
count = views.length;
...
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
...
measureChild(child, params, myWidth, myHeight);
...
}
}
...
setMeasuredDimension(width, height);
}
源碼如上所示,一共會(huì)對(duì)child進(jìn)行兩次測(cè)量。
1、在測(cè)量之前會(huì)先準(zhǔn)備數(shù)據(jù),會(huì)根據(jù)不同的依賴關(guān)系對(duì)child進(jìn)行分類。將不包含水平依賴
的child添加進(jìn)mSortedHorizontalChildren
;將不包含垂直依賴
的child添加進(jìn)mSortedVerticalChildren
。
2、第一次測(cè)量,測(cè)量所有不包含水平依賴關(guān)系
的child。
3、第二次測(cè)量,測(cè)量所有不包含垂直依賴關(guān)系
的child。
解釋一下,這里的水平依賴關(guān)系
指的是RULES_VERTICAL
中定義的一些依賴關(guān)系,垂直依賴關(guān)系
指的是RULES_HORIZONTAL
中定義的一些依賴關(guān)系。
private static final int[] RULES_VERTICAL = {
ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
};
private static final int[] RULES_HORIZONTAL = {
LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
};
需要注意的是,mSortedHorizontalChildren
中添加的是不包含水平依賴關(guān)系
child,mSortedVerticalChildren
中添加的是不包含垂直依賴關(guān)系
child。如果有的child既沒有水平依賴關(guān)系
,也沒有垂直依賴關(guān)系
,那它就會(huì)被分別添加進(jìn)mSortedHorizontalChildren
和mSortedVerticalChildren
中,會(huì)被測(cè)量?jī)纱巍?/p>
經(jīng)常有人說LinearLayout
和RelativeLayout
比較的話,推薦使用LinearLayout
,因?yàn)?code>LinearLayout測(cè)量次數(shù)少。經(jīng)過前面的分析我們可以知道,在默認(rèn)情況
下,如果一個(gè)child什么額外的屬性也不設(shè)置,那么在LinearLayout
中只會(huì)被測(cè)量一次
,在RelativeLayout
中會(huì)被測(cè)量?jī)纱?/code>。這就是這個(gè)問題的答案了,你下次可以理直氣壯的講原理了。
總結(jié):
綜上所述,在RelativeLayout一次測(cè)量流程中,RelativeLayout的child最少會(huì)經(jīng)歷一次測(cè)量
(必須的),最多是兩次
。
如果沒有設(shè)置對(duì)應(yīng)的依賴關(guān)系,那么child大概率會(huì)被測(cè)量?jī)纱?/code>。
相關(guān)資料
RelativeLayout
demo:RelativeLayoutTestActivity
系列文章:
從源碼角度理解FrameLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解LinearLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解RelativeLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
從源碼角度理解ConstraintLayout#onMeasure對(duì)child的measure調(diào)用次數(shù)
ViewGroup在調(diào)用onMeasure時(shí),會(huì)先測(cè)量父View,還是會(huì)先測(cè)量子View?