View#requestLayout()

Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree. This should not be called while the view hierarchy is currently in a layout pass (isInLayout(). If layout  is happening, the request may be honored at the end of the current layout pass (and then layout will run again) or after the current frame is drawn and the next layout occurs. Subclasses which override this method should call the superclass method to handle possible request-during-layout errors correctly. This function is called when something changes that invalidates the view's layout. This schedules the layout delivery of the view tree. This function should not be called when the view hierarchy is in the middle of the current layout pass (isInLayout()). If the layout is in progress, the request may be executed either when the current layout passes (and then the layout runs again), or after the current frame is drawn and the next layout appears. Subclasses overriding this method should call superclass methods to properly handle possible request layout errors.Copy the code

The final result after calling View#requestLayout() :

Typically, additional views also perform the drawing process.

Minimization effect:

① the requestLayout method will be called for all views that invoke requestLayout() and all parent levels (up to ViewRootImpl) with the PFLAG_FORCE_LAYOUT and PFLAG_INVALIDATED flags.

② In the next refresh time, the labeled View will execute onMeasure and onLayout from top to bottom.

In practice, the situation is more complicated. Different viewgroups have different onMeasure and onLayout implementations, and there are dependencies between layouts at the same level, so the execution range and times of onMeasure and onLayout are uncertain. In some cases child’s onDraw is also executed.

When the PFLAG_FORCE_LAYOUT flag is cleared:

In the View#layout method, the PFLAG_FORCE_LAYOUT flag is cleared after the onLayout method is called.

When the PFLAG_INVALIDATED mark is removed:

In the View#layout method, the PFLAG_FORCE_LAYOUT flag is cleared after the onLayout method is called.

In ViewRootImpl# performDraw process, will be called to ThreadedRenderer# updateViewTreeDisplayList (View View) method, So this is going to read the PFLAG_INVALIDATED flag and assign View#mRecreateDisplayList, and then clear the PFLAG_INVALIDATED flag.

The next View#updateDisplayListIfDirty() method uses the mRecreateDisplayList variable, and mRecreateDisplayList is true.

1, View# requestLayout ()

/** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view * tree. This should not be called while the view hierarchy is currently in a layout * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the * end of the current layout pass (and then layout will run again) or after the current  * frame is drawn and the next layout occurs. * * <p>Subclasses which override this method should call the superclass method to * handle possible request-during-layout errors correctly.</p> */ @CallSuper public void requestLayout() { if (mMeasureCache ! = null) mMeasureCache.clear(); // AttachInfo#mViewRequestingLayout is used to trace the View that originally initiated the requestLayout. if (mAttachInfo ! = null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot ! = null && viewRoot.isInLayout()) { if (! viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent ! = null && ! mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo ! = null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; }}Copy the code

(1) clear mMeasureCache

(2) to mAttachInfo mViewRequestingLayout assignment,

③ add the PFLAG_FORCE_LAYOUT and PFLAG_INVALIDATED flags to mPrivateFlags.

④ Call mParent’s requestLayout and do the same for mParent.

The net effect is to call requestLayout’s view and its parent (up to DecorView, ViewRootImpl), PFLAG_FORCE_LAYOUT, PFLAG_INVALIDATED flags are added to their mPrivateFlags.

2. The ViewRootImpl#requestLayout method is eventually called:


@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

Copy the code

Here mLayoutRequested to true. Then call scheduleTraversals();


ViewRootImpl#performTraversals()

ViewRootImpl#measureHierarchy()

Copy the code

3, ViewRootImpl:


private boolean mInLayout = false;

ArrayList<View> mLayoutRequesters = new ArrayList<View>();

boolean mHandlingLayoutInLayoutRequest = false;

Copy the code

To verify

Next, let’s verify it through an example:

Simple layout: FrameLayout

Layout 1:

<? The XML version = "1.0" encoding = "utf-8"? > <com.tinytongtong.androidstudy.measure.view.CustomLinearLayout 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" android:orientation="vertical" tools:context=".measure.RequestLayoutTestActivity"> <com.tinytongtong.androidstudy.measure.view.CustomFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.tinytongtong.androidstudy.measure.view.CustomSingleView android:id="@+id/view1" android:layout_width="100dp" android:layout_height="100dp" android:background="@color/background_wtf" /> <com.tinytongtong.androidstudy.measure.view.CustomTextView android:id="@+id/view2" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="10dp" android:layout_marginTop="130dp" android:background="@color/background_wtf" android:gravity="center" android:text="text"  /> <com.tinytongtong.androidstudy.measure.view.CustomButton android:id="@+id/view3" android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="right" /> </com.tinytongtong.androidstudy.measure.view.CustomFrameLayout> <com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp"> <com.tinytongtong.androidstudy.measure.view.CustomHelloView android:layout_width="100dp" android:layout_height="100dp" android:background="@color/background_warn" /> <com.tinytongtong.androidstudy.measure.view.CustomWorldView android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="right" android:background="@color/background_info" /> </com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout> </com.tinytongtong.androidstudy.measure.view.CustomLinearLayout>Copy the code

Log1:


E/CustomLayout-View: onMeasure widthSpecSize:275, widthSpecMode:1073741824, heightSpecSize:275, heightSpecMode:1073741824

E/CustomLayout-Frame: onMeasure widthSpecSize:1080, widthSpecMode:1073741824, heightSpecSize:1823, heightSpecMode:-2147483648

E/CustomLayout-Linear: onMeasure widthSpecSize:1080, widthSpecMode:1073741824, heightSpecSize:1823, heightSpecMode:1073741824

E/CustomLayout-View: onLayout changed:false, l:0, t:0, r:275, b:275

E/CustomLayout-Frame: onLayout changed:false, l:0, t:0, r:1080, b:633

E/CustomLayout-Linear: onLayout changed:false, l:0, t:0, r:1080, b:1823

E/CustomLayout-View: onDraw

Copy the code
Phenomenon:

After a child calls requestLayout:

(1) Call its onMeasure first, and then call its parent’s onMeasure from bottom up;

The onLayout method of the child is called; Finally, the onLayout method of each parent is called from the bottom up.

④ Child’s onDraw method is called

Principle analysis:
1. The onMeasure will be called first, and then the parent at all levels will call the onMeasure in turn;
--> ViewRootImpl#doTraversal --> ViewRootImpl#performTraversals --> ViewRootImpl#measureHierarchy: // Ask host how big it wants to be. LayoutRequested to true. --> ViewRootImpl#performMeasure:! DecorView#measure(int widthMeasureSpec Int widthMeasureSpec --> View#measure(int widthMeasureSpec, int widthMeasureSpec) : The onMeasure method is called when the view has the PFLAG_FORCE_LAYOUT flag or needsLayout is true. Either call it immediately or add PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT to call onMeasure first in onLayout. ----① Read the PFLAG_FORCE_LAYOUT flag from mPrivateFlags. If the result is true and there is no width and height in the cache, then call its own onMeasure method. ----② Obtain the value of needsLayout. If it is true, the procedure (1) is also performed. If there is width and height information in the cache at this time, add the PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT flag to mPrivateFlags3. Used in the Layout method. --> DecorView#onMeasure(int widthMeasureSpec, int heightMeasureSpec) --> FrameLayout#onMeasure(int widthMeasureSpec, int heightMeasureSpec) --> ViewGroup#measureChildWithMargins --> View#measure ...Copy the code

Finally, the View that initiated the requestLayout is called.

PFLAG_LAYOUT_REQUIRED is not added to mPrivateFlags because the View has the PFLAG_FORCE_LAYOUT flag and needsLayout is false. The PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT flag is not added to mPrivateFlags3, so the onLayout method is not called, and onMeasure is not called again when onLayout is called.

2. Call child’s onLayout method; Finally, the onLayout method of each parent is called.

In the ViewGroup#onLayout method, we usually call child’s onLayout first and then print our own log.

Child onDraw ();
--> Display traversal --> Display traversal --> Display traversal --> Display traversal --> Display traversal --> Display traversal: CancelDraw: false. --> viewrootimp #draw(Boolean fullRedrawNeeded) --> renderer #draw(View View, DecorView --> ThreadedRenderer#updateRootDisplayList(View View, DrawCallbacks) DrawCallbacks callbacks) -- - > ThreadedRenderer# updateViewTreeDisplayList View (View) : Read the PFLAG_INVALIDATED flag and assign #View#mRecreateDisplayList, then clear the PFLAG_INVALIDATED flag. --> View#updateDisplayListIfDirty() : mRecreateDisplayList is used, and mRecreateDisplayList is true. --> DecorView#draw(Canvas canvas): --> View#draw(Canvas canvas) --> View#dispatchDraw(Canvas canvas) --> ViewGroup#dispatchDraw(Canvas canvas) --> ViewGroup#drawChild(Canvas canvas, View child, long drawingTime) --> View#draw(Canvas canvas, ViewGroup parent, long drawingTime) --> View#updateDisplayListIfDirty() --> ViewGroup#dispatchDraw(Canvas canvas) --> ViewGroup#drawChild(Canvas canvas, View child, long drawingTime) ......Copy the code

The final call to child’s onDraw.

Layout 2: RealtiveLayout

<? The XML version = "1.0" encoding = "utf-8"? > <com.tinytongtong.androidstudy.measure.view.CustomLinearLayout 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" android:orientation="vertical" tools:context=".measure.RequestLayoutTestActivity"> <com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.tinytongtong.androidstudy.measure.view.CustomSingleView android:id="@+id/view1" android:layout_width="100dp" android:layout_height="100dp" android:background="@color/background_wtf" /> <com.tinytongtong.androidstudy.measure.view.CustomTextView android:id="@+id/view2" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="10dp" android:layout_marginTop="130dp" android:background="@color/background_wtf" android:gravity="center" android:text="text"  /> <com.tinytongtong.androidstudy.measure.view.CustomButton android:id="@+id/view3" android:layout_width="100dp" android:layout_height="100dp" android:layout_alignParentRight="true" /> </com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout> <com.tinytongtong.androidstudy.measure.view.CustomFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp"> <com.tinytongtong.androidstudy.measure.view.CustomHelloView android:layout_width="100dp" android:layout_height="100dp" android:background="@color/background_warn" /> <com.tinytongtong.androidstudy.measure.view.CustomWorldView android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="right" android:background="@color/background_info" /> </com.tinytongtong.androidstudy.measure.view.CustomFrameLayout> </com.tinytongtong.androidstudy.measure.view.CustomLinearLayout>Copy the code

Log2: CustomLayout-view is the child that initiates the requestLayout.


E/CustomLayout-View: onMeasure widthSpecSize:275, widthSpecMode:1073741824, heightSpecSize:1823, heightSpecMode:-2147483648

E/CustomLayout-View: onMeasure widthSpecSize:275, widthSpecMode:1073741824, heightSpecSize:275, heightSpecMode:1073741824

E/CustomLayout-Relative: onMeasure widthSpecSize:1080, widthSpecMode:1073741824, heightSpecSize:1823, heightSpecMode:-2147483648

E/CustomLayout-Linear: onMeasure widthSpecSize:1080, widthSpecMode:1073741824, heightSpecSize:1823, heightSpecMode:1073741824

E/CustomLayout-View: onLayout changed:false, l:0, t:0, r:275, b:275

E/CustomLayout-TextView: onMeasure widthSpecSize:275, widthSpecMode:1073741824, heightSpecSize:275, heightSpecMode:1073741824

E/CustomLayout-TextView: onLayout changed:false, l:28, t:358, r:303, b:633

E/CustomLayout-Button: onMeasure widthSpecSize:275, widthSpecMode:1073741824, heightSpecSize:275, heightSpecMode:1073741824

E/CustomLayout-Button: onLayout changed:false, l:805, t:0, r:1080, b:275

E/CustomLayout-Relative: onLayout changed:false, l:0, t:0, r:1080, b:633

E/CustomLayout-Linear: onLayout changed:false, l:0, t:0, r:1080, b:1823

Copy the code
Phenomenon:

After a child calls requestLayout:

(1) The onMeasure will be called twice, and then the onMeasure will be called once from the bottom up.

The onLayout method of the child will be called. OnMeasure and onLayout will be called.

③ Finally, call the onLayout method of all parent levels from bottom up.

Principle analysis:
1. The child is called onMeasure twice first: this is because its parent is RelativeLayout and the child is measured twice.

A measure process triggered primarily by the PFLAG_FORCE_LAYOUT tag.

ViewRootImpl#doTraversal ViewRootImpl#performTraversals ViewRootImpl#measureHierarchy: // Ask host how big it wants to be. LayoutRequested to true. ViewRootImpl#performMeasure:! DecorView#measure(int widthMeasureSpec, int heightMeasureSpec) View#measure(int widthMeasureSpec, Int heightMeasureSpec) : onMeasure is called when the view has the PFLAG_FORCE_LAYOUT flag or needsLayout is true. Either call it immediately or add PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT to call onMeasure first in onLayout. ----① Read the PFLAG_FORCE_LAYOUT flag from mPrivateFlags. If the result is true and there is no width and height in the cache, then call its own onMeasure method. ----② Obtain the value of needsLayout. If it is true, the procedure (1) is also performed. If there is width and height information in the cache at this time, add the PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT flag to mPrivateFlags3. Used in the Layout method. ----③ If the condition ① or ② is true, PFLAG_LAYOUT_REQUIRED is added to mPrivateFlags, which executes the subsequent onLayout. DecorView#onMeasure(int widthMeasureSpec, int heightMeasureSpec) FrameLayout#onMeasure(int widthMeasureSpec, int heightMeasureSpec) ViewGroup#measureChildWithMargins View#measure ...Copy the code

Finally, the View that initiated the requestLayout is called.

Child views whose needsLayout value is true will add PFLAG_LAYOUT_REQUIRED to mPrivateFlags, MPrivateFlags3 will be tagged with PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT, and onMeasure will be called first when their onLayout executes.

If the parent’s layout does not change, try not to call the onMeasure method of the irrelevant View.

OnMeasure = onMeasure; onMeasure = onMeasure;

The ViewGroup#onMeasure method calls the onMeasure of the child first and then determines its width and height. And then print our own log.

3. Then execute the Layout process.

ViewGroup#onLayout iterates through the layout/onLayout method that calls child.

MPrivateFlags3 has the PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT flag, so onMeasure is called before onLayout.

--> execute traversal --> execute traversal: DidLayout to true - > ViewRootImpl# performLayout (WindowManager. LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) --> DecorView#layout(int l, int t, int r, int b) --> ViewGroup#layout(int l, int t, int r, int b) --> View#layout(int l, int t, int r, int b) --> DecorView#onLayout(boolean changed, int left, int top, int right, int bottom) --> FrameLayout#onLayout(boolean changed, int left, int top, int right, Int left, int top, int right, int bottom, Boolean forceLeftGravity: Iterating calls the Layout method of Child. --> View#layout(int l, int t, int r, int b) --> ViewGroup#layout(int l, int t, int r, int b) ...Copy the code

Finally, the View that initiated the requestLayout is called.

The parent of a child is a RealtiveLayout. View#layout, the equivalent of a child, calls onMeasure first followed by onLayout.

public void layout(int l, int t, int r, int b) { if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) ! = 0) { // true onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }... // The onLayout method is then called. if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); . } // clear the PFLAG_FORCE_LAYOUT flag mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; }Copy the code
4. Finally, call the onLayout method of each parent.

In the ViewGroup#onLayout method, we usually call child’s onLayout first and then print our own log.

The demo address

Gitee.com/tinytongton…