- Recently in a
Golang
little lit up, to clean up the source material
- The analysis results are based on
Audroid API 26
requestLayout()
SOURCE Analysis
- If there is a button on a page, click on the button on the one
view.requestLayout()
, this view
execution method is as follows:
InvalidateTextView------onMeasureInvalidateTextView------onMeasureInvalidateTextView-------layoutInvalidateTextView--------onLayoutInvalidateTextView----------drawInvalidateTextView------------onDraw
view.requestLayout()
Details of the method
@CallSuper public void Requestlayout () {//clears the drawn cache if (Mmeasurecache! = null) mmeasurecache.clear (); if (mattachinfo! = null && mattachinfo.mviewrequestinglayout = = null) {//only if the request is triggered in the layout logic, if this is the view that requested it , instead of the view in its parent hierarchy Viewrootimpl Viewroot = Getviewrootimpl (); If you request two times in a row, one of them automatically returns! if (viewroot! = null && viewroot.isinlayout ()) {if (!viewroot.requestlayoutduringlayout (this)) { Return }} mattachinfo.mviewrequestinglayout = this; }//todo sets the marker bit for the current view pflag_force_layout mprivateflags |= pflag_force_layout; Mprivateflags |= pflag_invalidated; if (mparent! = null &&!mparent.islayoutrequested ()) {//TODO request layout to parent container here is the request layout to the parent container, which is called the parent container's Requestlay Out method, add the pflag_force_layout tag bit for the parent container, and the parent container calls its parent container's Requestlayout method, which is the Requestlayout event layer to pass up, until Decorview, the root view, And the root view will be passed to Viewrootimpl, that is, the r of the child viewThe Equestlayout event will eventually be received and processed by Viewrootimpl Mparent.requestlayout (); } if (Mattachinfo! = null && Mattachinfo.mviewrequestinglayout = = this) {Mattachinfo.mviewreque Stinglayout = null; } }
- 1. If the cache is not
null
, clear the drawn cache
if (mMeasureCache != null) mMeasureCache.clear();
- 2, here to determine whether in
layout
, if it is, return, it can be understood as: if a continuous request two times, and one of them is layout
in, one return! This is done to save performance
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { //只有在布局逻辑中触发请求,如果这是请求它的视图,而不是其父层次结构中的视图 ViewRootImpl viewRoot = getViewRootImpl(); //如果连续请求两次,其中一次自动返回! if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; }
- 3, for the current setting of the
view
mark bit PFLAG_FORCE_LAYOUT
, about |=
the symbol: a|=b
the meaning of A and B bitwise or then assigned to a bitwise OR of the meaning is to first A and b are replaced by 2, and then use or operation, equivalent toa=a|b
mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED;
- 4. Request a layout to the parent container, that is,
ViewGroup
the method that calls the parent container, adds a tag bit to the parent container, and the requestLayout()
parent container calls the method of PFLAG_FORCE_LAYOUT
its parent container, requestLayout()
that is, the requestLayout()
event layer layer is passed up until DecorView
, that is View
View
, the root The event will be passed ViewRootImpl,
on to the child view and requestLayout()f
will eventually be ViewRootImpl.requestLayout()
received and processed.
if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); }
5. ViewRootImpl.requestLayout()
Method Details
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { // 检查是否在主线程,不在的话,抛出异常 checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
- 1, check whether in the main thread, if not, throw an exception
checkThread();
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); }}
- 2, finally go to this method
ViewRootImpl.scheduleTraversals()
, in which you can see a very interesting line of code
mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
, where there is an object mTraversalRunnable
that will be re-measured, laid out and drawn, and the specific process can be seen in this article Android source code analysis (view drawing process)
// requestLayout() 会调用这个方法 void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 最终调用的是这个方法 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }}
There is a problem, I first throw the conclusion, the requessLayout() 、invalidate()、postInvalidate()
end of the bottom call is ViewRootImpl.scheduleTraversals()
the method, why only requessLayout()
the implementation of onMeasure() onLayout() onDraw()
these methods?
-
About the view.measure ()
method: In front we know mprivateflags |= pflag_force_layout
so Forcelayout = True
, which is the execution of onmeasure (Widthmeasurespec, Heightmeasurespec),
, when the execution is finished, and finally the tag bit is set to mprivateflags |=pflag_layout_required
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... // requestLayout的方法改变的 mPrivateFlags |= PFLAG_FORCE_LAYOUT; 所以 forceLayout = true final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; ... if (forceLayout || needsLayout) { ... if (cacheIndex < 0 || sIgnoreMeasureCache) { //最终会走到这方法来 onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } // 接着最后为标记位设置为PFLAG_LAYOUT_REQUIRED mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } ... }
- About the
view.layout()
method: Determine if the marker bit is PFLAG_LAYOUT_REQUIRED
, if there is, the View
layout, that is, go to onLayout(changed, l, t, r, b);
, finally clear the markmPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
@SuppressWarnings ({"Unchecked"}) public void layout (int l, int t, int r, int b) {if (MPrivateFlags3 & PFL ag3_measure_needed_before_layout)! = 0) {//second call to this method,, Onmeasure (Moldwidthmeasurespec, moldheightm EASURESPEC); MPRIVATEFLAGS3 &= ~pflag3_measure_needed_before_layout; } int oldl = Mleft; int Oldt = Mtop; int oldb = Mbottom; int oldr = Mright; Boolean changed = Islayoutmodeoptical (mparent)? Setopticalframe (L, T, R, B): Setframe (L, T, R, b); Determines whether the marker bit is pflag_layout_required, and if so, the view is laid out if (changed | | (Mprivateflags & pflag_layout_required) = = pflag_layout_required) {onlayout (changed, L, T, R, b); if (Shoulddrawroundscrollbar ()) {if (mroundscrollbarrenderer = = null) {Mroundscroll Barrenderer = new Roundscrollbarrenderer (this); }} else {MroundscrollbarRenderer = null; }//OnLayout method completes, clear pflag_layout_required mark bit mprivateflags &= ~pflag_layout_required; }////finally clear pflag_force_layout mark bit mprivateflags &= ~pflag_force_layout; MPRIVATEFLAGS3 |= pflag3_is_laid_out; ... }
- The above is
requestLayout()
the result of the analysis: view
call this method, in fact, from the view
tree to re-measure, layout, draw these three processes.
I made a picture.
Principle of Requestlayout (). jpg
invalidate()源码分析
view.invalidate()
; Inherit a textview, then override the method, set a but, simultaneous request method, print log: The result of the output of the request once
InvalidateTextView----------drawInvalidateTextView------------onDraw
- Method Details:
view.invalidate()
public void invalidate() { invalidate(true); }
- Whether the drawing cache for the view should also be invalid. Set to true for completely invalid, but can be set to False if the contents or dimensions of the view have not changed.
public void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); }
invalidateInternal()
Method Details: In fact, the key method isinvalidateChild()
void invalidateinternal (int l, int t, int r, int b, Boolean invalidatecache, Boolean fullinvalidate) {if (mGh Ostview = null) {Mghostview.invalidate (true); Return }//To determine whether it is visible, whether in the animation, is not viewgroup, three items satisfy an item, return directly if (Skipinvalidate ()) {return; }//determines whether the sub-view needs to be redrawn based on the view's marker bit, and if the view has no changes, it does not need to redraw if (Mprivateflags & (Pflag_drawn | pflag_has_bounds) = = (Pflag_drawn | Pflag_has_bounds) | | (Invalidatecache && (mprivateflags & pflag_drawing_cache_valid) = = Pflag_drawing_cache_valid) | | (Mprivateflags & pflag_invalidated)! = pflag_invalidated | | (Fullinvalidate && isopaque ()! = Mlastisopaque)) {if (fullinvalidate) {mlastisopaque = Isopaque (); Mprivateflags &= ~pflag_drawn; }//Set pflag_dirty mark bit mprivateflags |= pflag_dirty; if (invalidatecache) {mprivateflags |= pflag_invalidated; MPrivateflags &= ~pflag_drawing_cache_valid; }//Pass the area that needs to be redrawn to the parent container//Propagate The damage rectangle to the the parent view. Final Attachinfo ai = mattachinfo; Final viewparent p = mparent; if (P! = NULL && AI! = null && l < R && T < b) {final Rect damage = Ai.mtmpinva Lrect; Damage.set (L, T, R, b); Call the parent container's method and pass the event p.invalidatechild up (this, damage); }//Damage the entire projection receiver, if necessary. Damage the entire projection receiver, if not required. if (mbackground! = null && mbackground.isprojected ()) {final View receiver = Getprojectionreceiver (); if (receiver! = null) {receiver.damageinparent (); } } } }
- 1, judging whether it is visible, whether in the animation, whether it is not
ViewGroup
, three items to meet a, direct return, this method can also know, invalidate()
against IS View
, and notViewGroup
private boolean skipInvalidate() { return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null && (!(mParent instanceof ViewGroup) || !((ViewGroup) mParent).isViewTransitioning(this));}
- 2, through the view mark bit to determine whether the child view needs to be redrawn, if there is no change, then do not need to redraw
mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)
- 3. The area that needs repainting is passed to the parent container, passing the event up, and remember that the
damage
variable is definitely not here null
, otherwise the null pointer exception will be thrown directly inside the method.
p.invalidateChild(this, damage);
- 4, damage the entire projection receiver, if not required.
mBackground.isProjected()
: Does this picture need to be projected?
if (mBackground != null && mBackground.isProjected()) { final View receiver = getProjectionReceiver(); if (receiver != null) { receiver.damageInParent(); } }
- The key approach:
ViewRootImpl.invalidateChild(this, damage);
@Override public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); }
Invalidatechildinparent (null, dirty), adjusted the coordinates of offset and union, and then saved the dirty area information in Mdirty, and finally called the The Scheduletraversals ()
method, which triggers the view's workflow, is not executed because the measure and layout tags are not added, so the measure, layout process does not execute, but directly from draw The
process begins.
@Overridepublic ViewParent invalidateChildInParent(int[] location, Rect dirty) { // 检查线程,不是ui线程,直接抛出异常 checkThread(); if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty); if (dirty == null) { invalidate(); return null; } else if (dirty.isEmpty() && !mIsAnimating) { return null; } if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); dirty = mTempRect; if (mCurScrollY != 0) { // 将dirty中的坐标转化为父容器中的坐标,考虑mScrollX和mScrollY的影响 dirty.offset(0, -mCurScrollY); } if (mTranslator != null) { mTranslator.translateRectInAppWindowToScreen(dirty); } if (mAttachInfo.mScalingRequired) { dirty.inset(-1, -1); } } //进行了offset和union对坐标的调整 invalidateRectOnScreen(dirty); return null;}
- 1, check the thread, not the UI thread, throws an exception directly. and
requestLayout()
the same
checkThread();
- 2, if it is from the invalidate () method, then the dirty is definitely NOT NULL because if NULL, the place where the previous method was called throws a null pointer exception
if (dirty == null) { invalidate(); return null; }
- 3. Cancel the rectangle by adding dx=0 to its left and right coordinates, and adding mcurscrolly to its top and bottom coordinates.
dirty.offset(0, -mCurScrollY);
- 4. Adjust the coordinates of offset and union
invalidateRectOnScreen(dirty);
- About the
invalidateRectOnScreen(dirty)
method: The final key method: Scheduletraversals ();
private void Invalidaterectonscreen (Rect dirty) {final Rect localdirty = Mdirty; if (!localdirty.isempty () &&!localdirty.contains (Dirty)) {mattachinfo.msetignoredirtystate = true; Mattachinfo.mignoredirtystate = true; }//Add the new dirty rect to the current one//Add a fresh dirty rect to the present Rect localdirty.union (DIRTY.L EFT, Dirty.top, Dirty.right, Dirty.bottom); Intersect with the bounds of the window to skip/updates that lie outside of the visible region final F Loat Appscale = Mattachinfo.mapplicationscale; Final Boolean intersected = localdirty.intersect (0, 0, (int) (Mwidth * Appscale + 0.5f), (int) (Mheight * A Ppscale + 0.5f)); if (!intersected) {localdirty.setempty (); } if (!mwilldrawsoon && (intersected | | misanimating)) {scheduletraversals (); } }
- The final key method is to call this object, which is the same as the
ViewRootImpl.scheduleTraversals();
mTraversalRunnable
underlying method of the requessLaout()
final call, except for the invalidate()
measure()
tag bits that are not added and the layout()
subsequent process will not execute! Specific process can see this article Android source code Analysis (view of the drawing process)
- The invocation of the method causes the
View
tree to redraw, is often used for internal calls (such as setvisiblity ()), or needs to refresh the interface, it needs to be called in the main thread (that is, the UI thread), invalidate
there are multiple overloaded methods, but eventually the invalidateInternal
method is called. Inside this method, a series of judgments is made to determine whether a redraw is View
required, then a View
bit is marked for the setting, and the area to be redrawn is passed to the parent container, which is the method that invokes the parent container invalidateChild
.
I made a picture.
Principle of invalidate (). jpg
Postinvalidate () Source code parsing
view.postInvalidate()
Inherit a textview, then override the method, set a but, simultaneous request method, print log: The result of the output of the request once
InvalidateTextView----------drawInvalidateTextView------------onDraw
view.postInvalidate()
Details, because the method is public, can also call a time, how long the delay begins to execute, here is Delaymilliseconds, MS
public void postInvalidate() { postInvalidateDelayed(0); }
view.postInvalidateDelayed()
attachInfo
execution continues only when it is not NULL, that is, only when the view is added to the window to be redrawn, because this is an asynchronous method, and if the view has not been added to the window to notify redraw, the error will occur, so make a judgment call!
public void postInvalidateDelayed(long delayMilliseconds) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } }
ViewRootImpl.dispatchInvalidateDelayed()
With handler, an asynchronous message is sent to the main thread, which obviously sends a msg_invalidate, which notifies the main thread to refresh the view
/** * 用了Handler,发送了一个异步消息到主线程,显然这里发送的是`MSG_INVALIDATE`,即通知主线程刷新视图 * @param view 只有 postInvalidate() 使用了handler 来发送消息 * @param delayMilliseconds */ public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); }
- Notifies the object to go
invalidate()
, the bottom is also called is invalidate()
, only uses mHandler
sends the message, here sends to the main thread, to call the invalidate()
method
case MSG_INVALIDATE: //通知对象去 invalidate ,底层也是调用的是 invalidate,只不过使用了handler发送消息 ((View) msg.obj).invalidate(); break;0
The explanation of the method postInvalidate
is that it is called in a non-UI thread, but the underlying use is invalidate()
to ViewRootImpl的内部handler: ViewRootHandler
send the message, but it can also be used in the main thread, if it is forced to use in the main thread, there is a handler
job inside, is not a bit wasteful, right!
postInvalidate()
This method can also be used in the main thread
I made a picture.
Principle of postinvalidate (). jpg
-
Finally shows the
-
invalidate (), Postinvalidate (), Requestlayout ()
, and at the bottom of the call is Viewrootimpl.scheduletraversals ()
This method, requestlayout
because it is set measure
and layout The tag bit of the
, so requestlayout
can go through the drawing process again
-
postinvalidate ()
bottom through Handler
the work of the non-UI thread is called invalidate ()
.
-
invalidate (), Requestlayout ()
, the method checks whether the UI
thread, or not, throws an exception directly, so they can only be used in the UI thread, The Postinvalidate ()
can be used in both the UI thread and non-UI threads.
-
View
itself is not suitable for an area, for example, "Layoutparams has changed, need to re-measure, layout and draw three processes, then use this method is the most appropriate
Requestlayout () '.
- If the current view is refreshed, the current view is redrawn, and the measurement and layout process is not performed. Using the
invalidate () method tends to be more efficient than the Requestlayout () method only if you need a view
redraw instead of a measurement