// 2. 窗口宽高 int desiredWindowWidth; int desiredWindowHeight;
Rect frame = mWinFrame; if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true;
final Configuration config = mContext.getResources().getConfiguration(); if (shouldUseDisplaySize(lp)) { // NOTE -- system code, won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } elseif (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { // For wrap content, we have to remeasure later on anyways. Use size consistent with // below so we get best use of the measure cache. desiredWindowWidth = dipToPx(config.screenWidthDp); desiredWindowHeight = dipToPx(config.screenHeightDp); } else { // After addToDisplay, the frame contains the frameHint from window manager, which // for most windows is going to be the same size as the result of relayoutWindow. // Using this here allows us to avoid remeasuring after relayoutWindow desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); }
privatestaticintgetRootMeasureSpec(int windowSize, int rootDimension){ int measureSpec; switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
// Optimize layout by avoiding an extra EXACTLY pass when the view is // already measured as the correct size. In API 23 and below, this // extra pass is required to make LinearLayout re-distribute weight. finalboolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec; finalboolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; finalboolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); finalboolean needsLayout = specChanged && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if (forceLayout || needsLayout) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }
// flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { thrownew IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); }
protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed){ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 根据 attachInfo 信息确定,当前 view 是发起 requestLayout 的 view 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()) { // ViewRootImpl 正在进行 layout 操作 if (!viewRoot.requestLayoutDuringLayout(this)) { // 在 layout 的过程中,如果需要调用了 requestLayout ,那么就进行不继续进行了, // 而是在 layout 阶段进行一次完整的 measure/layout 过程。 return; } } mAttachInfo.mViewRequestingLayout = this; } // 标志位写 1 mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) { // 递归的向上调用 mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } } /** * Called by {@link android.view.View#requestLayout()} if the view hierarchy is currently * undergoing a layout pass. requestLayout() should not generally be called during layout, * unless the container hierarchy knows what it is doing (i.e., it is fine as long as * all children in that container hierarchy are measured and laid out at the end of the layout * pass for that container). If requestLayout() is called anyway, we handle it correctly * by registering all requesters during a frame as it proceeds. At the end of the frame, * we check all of those views to see if any still have pending layout requests, which * indicates that they were not correctly handled by their container hierarchy. If that is * the case, we clear all such flags in the tree, to remove the buggy flag state that leads * to blank containers, and force a second request/measure/layout pass in this frame. If * more requestLayout() calls are received during that second layout pass, we post those * requests to the next frame to avoid possible infinite loops. * * <p>The return value from this method indicates whether the request should proceed * (if it is a request during the first layout pass) or should be skipped and posted to the * next frame (if it is a request during the second layout pass).</p> * * @param view the view that requested the layout. * * @return true if request should proceed, false otherwise. */ booleanrequestLayoutDuringLayout(final View view){ if (view.mParent == null || view.mAttachInfo == null) { // Would not normally trigger another layout, so just let it pass through as usual returntrue; } // 将当前 view 添加到这个集合中 if (!mLayoutRequesters.contains(view)) { mLayoutRequesters.add(view); } // 在 performLayout 阶段,mLayoutRequesters 中需要测量的 view 的数量大于 0,那么这个值 // 会设置为 true ,其余情况均是 false if (!mHandlingLayoutInLayoutRequest) { // Let the request proceed normally; it will be processed in a second layout pass // if necessary returntrue; } else { // Don't let the request proceed during the second layout pass. // It will post to the next frame instead. returnfalse; } }
/** * <p>Indicates whether or not this view's layout will be requested during * the next hierarchy layout pass.</p> * * @return true if the layout will be forced during next layout pass */ publicbooleanisLayoutRequested(){ return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; }
isLayoutValid
1 2 3 4 5 6
/** * @return {@code true} if laid-out and not about to do another layout. */ booleanisLayoutValid(){ return isLaidOut() && ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == 0); }