From 3a5457cd7cf97171c3435a157f158eefa52bab0a Mon Sep 17 00:00:00 2001 From: Dave Miller Date: Fri, 29 Apr 2016 04:02:22 -0700 Subject: [PATCH] Add support for setChildren Summary: This adds support for UIManager.setChildren on Android like D2757388 added for iOS. Reviewed By: andreicoman11 Differential Revision: D3235369 fb-gh-sync-id: b538556ec4abdb606f9be26d1b74734046bca0cd fbshipit-source-id: b538556ec4abdb606f9be26d1b74734046bca0cd --- Libraries/Utilities/UIManager.js | 29 ------------ .../uimanager/NativeViewHierarchyManager.java | 45 +++++++++++++++++++ .../NativeViewHierarchyOptimizer.java | 22 +++++++++ .../react/uimanager/UIImplementation.java | 29 ++++++++++++ .../react/uimanager/UIManagerModule.java | 14 ++++++ .../react/uimanager/UIViewOperationQueue.java | 26 +++++++++++ 6 files changed, 136 insertions(+), 29 deletions(-) diff --git a/Libraries/Utilities/UIManager.js b/Libraries/Utilities/UIManager.js index d5450d158..6b94bfcb2 100644 --- a/Libraries/Utilities/UIManager.js +++ b/Libraries/Utilities/UIManager.js @@ -14,35 +14,6 @@ var UIManager = require('NativeModules').UIManager; var findNodeHandle = require('findNodeHandle'); -if (!UIManager.setChildren) { - - /** - * Index cache (used by setChildren()) - */ - UIManager._cachedIndexArray = function(size) { - var cachedResult = this._cachedIndexArray._cache[size]; - if (!cachedResult) { - var arr = []; - for (var i = 0; i < size; i++) { - arr[i] = i; - } - this._cachedIndexArray._cache[size] = arr; - return arr; - } else { - return cachedResult; - } - }; - UIManager._cachedIndexArray._cache = {}; - - /** - * Fallback setChildren() implementation for Android - */ - UIManager.setChildren = function(containerTag, createdTags) { - var indexes = this._cachedIndexArray(createdTags.length); - UIManager.manageChildren(containerTag, null, null, createdTags, indexes, null); - }; -} - const _takeSnapshot = UIManager.takeSnapshot; /** diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index 1df358fad..be555c82a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -388,6 +388,51 @@ public class NativeViewHierarchyManager { } } + /** + * Simplified version of constructManageChildrenErrorMessage that only deals with adding children + * views + */ + private static String constructSetChildrenErrorMessage( + ViewGroup viewToManage, + ViewGroupManager viewManager, + ReadableArray childrenTags) { + ViewAtIndex[] viewsToAdd = new ViewAtIndex[childrenTags.size()]; + for (int i = 0; i < childrenTags.size(); i++) { + viewsToAdd[i] = new ViewAtIndex(childrenTags.getInt(i), i); + } + return constructManageChildrenErrorMessage( + viewToManage, + viewManager, + null, + viewsToAdd, + null + ); + } + + /** + * Simplified version of manageChildren that only deals with adding children views + */ + public void setChildren( + int tag, + ReadableArray childrenTags) { + ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag); + ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(tag); + + for (int i = 0; i < childrenTags.size(); i++) { + View viewToAdd = mTagsToViews.get(childrenTags.getInt(i)); + if (viewToAdd == null) { + throw new IllegalViewOperationException( + "Trying to add unknown view tag: " + + childrenTags.getInt(i) + "\n detail: " + + constructSetChildrenErrorMessage( + viewToManage, + viewManager, + childrenTags)); + } + viewManager.addView(viewToManage, viewToAdd, i); + } + } + /** * See {@link UIManagerModule#addMeasuredRootView}. * diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java index 1297cc7e3..7b9741bd8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java @@ -14,6 +14,7 @@ import javax.annotation.Nullable; import android.util.SparseBooleanArray; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMapKeySetIterator; /** @@ -171,6 +172,27 @@ public class NativeViewHierarchyOptimizer { } } + /** + * Handles a setChildren call. This is a simplification of handleManagerChildren that only adds + * children in index order of the childrenTags array + */ + public void handleSetChildren( + ReactShadowNode nodeToManage, + ReadableArray childrenTags + ) { + if (!ENABLED) { + mUIViewOperationQueue.enqueueSetChildren( + nodeToManage.getReactTag(), + childrenTags); + return; + } + + for (int i = 0; i < childrenTags.size(); i++) { + ReactShadowNode nodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i)); + addNodeToNode(nodeToManage, nodeToAdd, i); + } + } + /** * Handles an updateLayout call. All updateLayout calls are collected and dispatched at the end * of a batch because updateLayout calls to layout-only nodes can necessitate multiple diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index b4b724240..20df2a161 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -323,6 +323,35 @@ public class UIImplementation { } } + /** + * An optimized version of manageChildren that is used for initial setting of child views. + * The children are assumed to be in index order + * + * @param viewTag tag of the parent + * @param childrenTags tags of the children + */ + public void setChildren( + int viewTag, + ReadableArray childrenTags) { + + ReactShadowNode cssNodeToManage = mShadowNodeRegistry.getNode(viewTag); + + for (int i = 0; i < childrenTags.size(); i++) { + ReactShadowNode cssNodeToAdd = mShadowNodeRegistry.getNode(childrenTags.getInt(i)); + if (cssNodeToAdd == null) { + throw new IllegalViewOperationException("Trying to add unknown view tag: " + + childrenTags.getInt(i)); + } + cssNodeToManage.addChildAt(cssNodeToAdd, i); + } + + if (!cssNodeToManage.isVirtual() && !cssNodeToManage.isVirtualAnchor()) { + mNativeViewHierarchyOptimizer.handleSetChildren( + cssNodeToManage, + childrenTags); + } + } + /** * Replaces the View specified by oldTag with the View specified by newTag within oldTag's parent. */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index b3109e66b..457fea3ef 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -231,6 +231,20 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements removeFrom); } + /** + * Interface for fast tracking the initial adding of views. Children view tags are assumed to be + * in order + * + * @param viewTag the view tag of the parent view + * @param childrenTags An array of tags to add to the parent in order + */ + @ReactMethod + public void setChildren( + int viewTag, + ReadableArray childrenTags) { + mUIImplementation.setChildren(viewTag, childrenTags); + } + /** * Replaces the View specified by oldTag with the View specified by newTag within oldTag's parent. * This resolves to a simple {@link #manageChildren} call, but React doesn't have enough info in diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index 1bfbbadf0..a5399a2e2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -180,6 +180,25 @@ public class UIViewOperationQueue { } } + private final class SetChildrenOperation extends ViewOperation { + + private final ReadableArray mChildrenTags; + + public SetChildrenOperation( + int tag, + ReadableArray childrenTags) { + super(tag); + mChildrenTags = childrenTags; + } + + @Override + public void execute() { + mNativeViewHierarchyManager.setChildren( + mTag, + mChildrenTags); + } + } + private final class UpdateViewExtraData extends ViewOperation { private final Object mExtraData; @@ -640,6 +659,13 @@ public class UIViewOperationQueue { new ManageChildrenOperation(reactTag, indicesToRemove, viewsToAdd, tagsToDelete)); } + public void enqueueSetChildren( + int reactTag, + ReadableArray childrenTags) { + mOperations.add( + new SetChildrenOperation(reactTag, childrenTags)); + } + public void enqueueRegisterAnimation(Animation animation) { mOperations.add(new RegisterAnimationOperation(animation)); }