/** * Handles a manageChildren call. This may translate into multiple manageChildren calls for * multiple other views. * * <p>NB: the assumption for calling this method is that all corresponding ReactShadowNodes have * been updated **but tagsToDelete have NOT been deleted yet**. This is because we need to use the * metadata from those nodes to figure out the correct commands to dispatch. This is unlike all * other calls on this class where we assume all operations on the shadow hierarchy have already * completed by the time a corresponding method here is called. */ public void handleManageChildren( ReactShadowNode nodeToManage, int[] indicesToRemove, int[] tagsToRemove, ViewAtIndex[] viewsToAdd, int[] tagsToDelete) { if (!ENABLED) { mUIViewOperationQueue.enqueueManageChildren( nodeToManage.getReactTag(), indicesToRemove, viewsToAdd, tagsToDelete); return; } // We operate on tagsToRemove instead of indicesToRemove because by the time this method is // called, these views have already been removed from the shadow hierarchy and the indices are // no longer useful to operate on for (int i = 0; i < tagsToRemove.length; i++) { int tagToRemove = tagsToRemove[i]; boolean delete = false; for (int j = 0; j < tagsToDelete.length; j++) { if (tagsToDelete[j] == tagToRemove) { delete = true; break; } } ReactShadowNode nodeToRemove = mShadowNodeRegistry.getNode(tagToRemove); removeNodeFromParent(nodeToRemove, delete); } for (int i = 0; i < viewsToAdd.length; i++) { ViewAtIndex toAdd = viewsToAdd[i]; ReactShadowNode nodeToAdd = mShadowNodeRegistry.getNode(toAdd.mTag); addNodeToNode(nodeToManage, nodeToAdd, toAdd.mIndex); } }
/** * For handling node removal from manageChildren. In the case of removing a layout-only node, we * need to instead recursively remove all its children from their native parents. */ private void removeNodeFromParent(ReactShadowNode nodeToRemove, boolean shouldDelete) { ReactShadowNode nativeNodeToRemoveFrom = nodeToRemove.getNativeParent(); if (nativeNodeToRemoveFrom != null) { int index = nativeNodeToRemoveFrom.indexOfNativeChild(nodeToRemove); nativeNodeToRemoveFrom.removeNativeChildAt(index); mUIViewOperationQueue.enqueueManageChildren( nativeNodeToRemoveFrom.getReactTag(), new int[] {index}, null, shouldDelete ? new int[] {nodeToRemove.getReactTag()} : null); } else { for (int i = nodeToRemove.getChildCount() - 1; i >= 0; i--) { removeNodeFromParent(nodeToRemove.getChildAt(i), shouldDelete); } } }
private void transitionLayoutOnlyViewToNativeView( ReactShadowNode node, @Nullable CatalystStylesDiffMap props) { ReactShadowNode parent = node.getParent(); if (parent == null) { node.setIsLayoutOnly(false); return; } // First, remove the node from its parent. This causes the parent to update its native children // count. The removeNodeFromParent call will cause all the view's children to be detached from // their native parent. int childIndex = parent.indexOf(node); parent.removeChildAt(childIndex); removeNodeFromParent(node, false); node.setIsLayoutOnly(false); // Create the view since it doesn't exist in the native hierarchy yet mUIViewOperationQueue.enqueueCreateView( node.getRootNode().getReactTag(), node.getReactTag(), node.getViewClass(), props); // Add the node and all its children as if we are adding a new nodes parent.addChildAt(node, childIndex); addNodeToNode(parent, node, childIndex); for (int i = 0; i < node.getChildCount(); i++) { addNodeToNode(node, node.getChildAt(i), i); } // Update layouts since the children of the node were offset by its x/y position previously. // Bit of a hack: we need to update the layout of this node's children now that it's no longer // layout-only, but we may still receive more layout updates at the end of this batch that we // don't want to ignore. Assertions.assertCondition(mTagsWithLayoutVisited.size() == 0); applyLayoutBase(node); for (int i = 0; i < node.getChildCount(); i++) { applyLayoutBase(node.getChildAt(i)); } mTagsWithLayoutVisited.clear(); }