From 529390b87c6239dd2b1f48bfb7e76fca81f529aa Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Wed, 16 Dec 2015 14:57:07 -0800 Subject: [PATCH] Fix children of AndroidView not being re-laid out after they call requestLayout Summary: In ReactNative, we are fully controlling layout of all the Views, not allowing Android to layout anything for us. This is done by making onLayout() of the top-level View in the hierarchy to be empty. This works fine because we explicitly call measure/layout for all the Views when they need to be re-measured or re-laid out. There is however one case where this doesn't happen automatically: some Android Views such as DrawerLayout or ActionBar have children that don't have shadow nodes associated with them (such as a title in ActionBar). This results in situations where children of AndroidView will call requestLayout but they will never get relaid out, because shadow hierarchy doesn't know about them. Example: ActionBar has a seTitle method that will internally call TextView.setTitle() and that TextView will call requestLayout because its size may have changed. However, that TextView will never be remeasured or relaid out. This diff is fixing it by keeping track of everyone who called requestLayout. Then, at the end of the update loop we go over the list a manually remeasure and relayout those Views. Not a huge fan of how this is implemented (there MUST be a better way) but this works with least efforts. I'll see if I can improve it later. Reviewed By: ahmedre Differential Revision: D2757485 --- .../react/flat/FlatUIViewOperationQueue.java | 12 ++++++ .../facebook/react/flat/FlatViewGroup.java | 39 ++++++++++++++++++- .../com/facebook/react/flat/StateBuilder.java | 2 + 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java index de18b70bd..7175633d6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java @@ -21,6 +21,14 @@ import com.facebook.react.uimanager.UIViewOperationQueue; /* package */ final class FlatUIViewOperationQueue extends UIViewOperationQueue { private final FlatNativeViewHierarchyManager mNativeViewHierarchyManager; + private final ProcessLayoutRequests mProcessLayoutRequests = new ProcessLayoutRequests(); + + private final class ProcessLayoutRequests implements UIOperation { + @Override + public void execute() { + FlatViewGroup.processLayoutRequests(); + } + } /** * UIOperation that updates DrawCommands for a View defined by reactTag. @@ -199,6 +207,10 @@ import com.facebook.react.uimanager.UIViewOperationQueue; enqueueUIOperation(new DropViews(viewsToDrop)); } + public void enqueueProcessLayoutRequests() { + enqueueUIOperation(mProcessLayoutRequests); + } + public DetachAllChildrenFromViews enqueueDetachAllChildrenFromViews() { DetachAllChildrenFromViews op = new DetachAllChildrenFromViews(); enqueueUIOperation(op); diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java index feb446935..b4157b54a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java @@ -10,6 +10,7 @@ package com.facebook.react.flat; import java.lang.ref.WeakReference; +import java.util.ArrayList; import javax.annotation.Nullable; @@ -46,12 +47,15 @@ import com.facebook.react.uimanager.ReactCompoundView; } } + private static final ArrayList LAYOUT_REQUESTS = new ArrayList<>(); + private @Nullable InvalidateCallback mInvalidateCallback; private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY; private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; private int mDrawChildIndex = 0; private boolean mIsAttached = false; + private boolean mIsLayoutRequested = false; /* package */ FlatViewGroup(Context context) { super(context); @@ -62,6 +66,16 @@ import com.facebook.react.uimanager.ReactCompoundView; super.detachAllViewsFromParent(); } + @Override + public void requestLayout() { + if (mIsLayoutRequested) { + return; + } + + mIsLayoutRequested = true; + LAYOUT_REQUESTS.add(this); + } + @Override public int reactTagForTouch(float touchX, float touchY) { for (NodeRegion nodeRegion : mNodeRegions) { @@ -167,7 +181,7 @@ import com.facebook.react.uimanager.ReactCompoundView; for (int viewToAdd : viewsToAdd) { if (viewToAdd > 0) { View view = ensureViewHasNoParent(viewResolver.getView(viewToAdd)); - addView(view, -1, ensureLayoutParams(view.getLayoutParams())); + addViewInLayout(view, -1, ensureLayoutParams(view.getLayoutParams()), true); } else { View view = ensureViewHasNoParent(viewResolver.getView(-viewToAdd)); attachViewToParent(view, -1, ensureLayoutParams(view.getLayoutParams())); @@ -179,6 +193,29 @@ import com.facebook.react.uimanager.ReactCompoundView; } } + /* package */ void processLayoutRequest() { + mIsLayoutRequested = false; + for (int i = 0, childCount = getChildCount(); i != childCount; ++i) { + View child = getChildAt(i); + if (!child.isLayoutRequested()) { + continue; + } + + child.measure( + MeasureSpec.makeMeasureSpec(child.getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getHeight(), MeasureSpec.EXACTLY)); + child.layout(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); + } + } + + /* package */ static void processLayoutRequests() { + for (int i = 0, numLayoutRequests = LAYOUT_REQUESTS.size(); i != numLayoutRequests; ++i) { + FlatViewGroup flatViewGroup = LAYOUT_REQUESTS.get(i); + flatViewGroup.processLayoutRequest(); + } + LAYOUT_REQUESTS.clear(); + } + private View ensureViewHasNoParent(View view) { ViewParent oldParent = view.getParent(); if (oldParent != null) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java index ccce7b24a..3875f01de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java @@ -84,6 +84,8 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; mOperationsQueue.enqueueDropViews(collectViewTags(mViewsToDrop)); mViewsToDrop.clear(); } + + mOperationsQueue.enqueueProcessLayoutRequests(); } /**