Under what circumstances will onMeasure
be executed?
Enter the measure
method of View
:
void measure(){<!-- --> boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec; boolean isSepcExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY & amp; & amp; MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec. getSize(widthMeasureSpec) & amp; & amp; getMeasuredHeight() == MeasureSpec. getSize(heightMeasureSpec); final boolean needsLayout = specChanged & amp; & amp; (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize); if(forceLayout || needLayout){<!-- --> int cacheIndex = forceLayout ? -1 : mMeasureCache. indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) {<!-- --> onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else {<!-- --> long value = mMeasureCache. valueAt(cacheIndex); setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } } }
When forceLayout=true
:
- Call
requestLayout
- Call
forceRequestLayout
When needsLayout=true
:
- When the length and width change
When is the onMeasure>
method called:
forceLayouy=true
- Or
mMeasureCache
has no current cache
So to sum up: when requestLayout
is called, the retest process will be tested. When forceLayout=false
, it will judge the value of mMeasureCache
. Now research Download this mMeasureCache
class View{<!-- --> LongSparseLongArray mMeasureCache; void measure(widthSpec, heightSpec){<!-- --> --- long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; int cacheIndex = forceLayout ? -1 : mMeasureCache. indexOfKey(key); if(cacheIndex<0){<!-- --> onMeasure(widthSpec, heightSpec); } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, widthSpec|heightSpec); --- } }
Here you can see that oldWidthMeasureSpec
and mMeasureCache
both cache the last value, so what is the difference between them? The difference is that oldWidthMeasureSpec>
not only Only spec
mode of measurement is cached and size
is cached. But mMeasureCache
only caches size
. From this line The code can be seen:
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & amp; 0xffffffffL;
The operation here is to eliminate the influence caused by spec
.
//If you don't believe me, you can try the following code public class Test {<!-- --> public static void main(String[] args) {<!-- --> long widthMeasureSpec = makeMeasureSpec(10,0); long heightMeasureSpec = makeMeasureSpec(20,0); long ss = widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; System.out.println("===========" + ss); } private static final int MODE_MASK = 0x3 << 30; public static int makeMeasureSpec(int size, int mode) {<!-- --> return (size & amp; ~MODE_MASK) | (mode & amp; MODE_MASK); } } //42949672980 //42949672980 //42949672980
When mPrivateFlags
will be assigned PFLAG_FORCE_LAYOUT
.
In the constructor of view viewGrouup
, it will actively assign a value once, and then it will give mProvateFlags
of the current View
when ViewGroup.addView
code>assign PFLAG_FORCE_LAYOUT
.
Why is onMeasure
executed twice?
void measure(int widthMeasureSpec, int heightMeasureSpec){<!-- --> ---- boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; if(forceLayout | needsLayout){<!-- --> onMeasure() } ---- } public void layout(int l, int t, int r, int b){<!-- --> --- mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; --- }
When the measure
method is triggered for the first time, forceLayoyt=true needsLayout=true
, but the layout
method has not yet been triggered.
When the measure>
method is triggered for the second time, forceLayout=true needsLayout=false
, so it will still enter the onMeasure
method. This time it will be executed layout
method. Then we will forceLayout
be equal to false
next time. The above analysis is an analysis of measure
How to prevent multiple calls to onMeasure
.
Now analyze how the measure
method is called multiple times externally:
When Activity
executes to the onResume
life cycle, it will execute the WindowManager.addView
operation, the specific implementation class of WindowManager
It is WindowManagerImpl
and then the addView
operation is handed over to the proxy class WindowManagerGlobal
, and then addView
of WindowManagerGlobal
The ViewRootImpl.setView
operation is executed in code> (the ViewRootImpl
object is also created at this time), and ViewRootImpl
will actively call once requestLayout
, which starts the first time view measurement layout drawing.
In setView
, ViewRootImpl.requestLayout
is actively called once. Note that this requestLayout
is the internal method of ViewRootImpl
, and view viewGroup
and requestLayout
are different. The performTraversals
method is called inside ViewRootImpl.requestLayout
:
class ViewRootImpl{<!-- --> void performTraversals(){<!-- --> if(layoutResuested){<!-- --> // mark 1 windowSizeMayChanged |= measureHierarchy(host,lp,res,desiredWindowWidth,desiredWindowHeight); } // mark 2 performMeasure() performLayout() } void measureHierarchy(){<!-- --> performMeasure() } }
From the execution logic of ViewRootImpl
, you can see that before executing performLayout
, he has called the performMeasure
method twice. So you now Just know why.