diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 080e7c0543b09..97287c3b27620 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15509,17 +15509,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * handle possible request-during-layout errors correctly.
*/ public void requestLayout() { - ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot != null && viewRoot.isInLayout()) { - viewRoot.requestLayoutDuringLayout(this); - return; + 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; + } } /** @@ -17999,6 +18009,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ final Point mPoint = new Point(); + /** + * Used to track which View originated a requestLayout() call, used when + * requestLayout() is called during layout. + */ + View mViewRequestingLayout; + /** * Creates a new set of attachment information with the specified * events handler and thread. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 85fab2859a0fa..9377cd4a6bf14 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1894,22 +1894,37 @@ public final class ViewRootImpl implements ViewParent, } /** - * Called by {@link android.view.View#requestLayout()} if the view hiearchy is currently - * undergoing a layout pass. requestLayout() should not be called during layout, but if it - * is called anyway, we handle the situation here rather than leave the hierarchy in an - * indeterminate state. The solution is to queue up all requests during layout, apply those - * requests as soon as layout is complete, and then run layout once more immediately. If + * 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. + * requests to the next frame to avoid possible infinite loops. + * + *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).
* * @param view the view that requested the layout. + * + * @return true if request should proceed, false otherwise. */ - void requestLayoutDuringLayout(final View view) { + boolean requestLayoutDuringLayout(final View view) { + if (view.mParent == null || view.mAttachInfo == null) { + // Would not normally trigger another layout, so just let it pass through as usual + return true; + } if (!mHandlingLayoutInLayoutRequest) { if (!mLayoutRequesters.contains(view)) { - Log.w("View", "requestLayout() called by " + view + " during layout pass"); mLayoutRequesters.add(view); } + return true; } else { Log.w("View", "requestLayout() called by " + view + " during second layout pass: " + "posting to next frame"); @@ -1919,6 +1934,7 @@ public final class ViewRootImpl implements ViewParent, view.requestLayout(); } }); + return false; } } @@ -1937,20 +1953,66 @@ public final class ViewRootImpl implements ViewParent, Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); + mInLayout = false; int numViewsRequestingLayout = mLayoutRequesters.size(); if (numViewsRequestingLayout > 0) { - // requestLayout() was called during layout: unusual, but try to handle correctly - mHandlingLayoutInLayoutRequest = true; + // requestLayout() was called during layout. + // If no layout-request flags are set on the requesting views, there is no problem. + // If some requests are still pending, then we need to clear those flags and do + // a full request/measure/layout pass to handle this situation. + + // Check state of layout flags for all requesters + ArrayList