5252 */
5353public class NativeViewHierarchyOptimizer {
5454
55+ private static class NodeIndexPair {
56+ public final ReactShadowNode node ;
57+ public final int index ;
58+
59+ NodeIndexPair (ReactShadowNode node , int index ) {
60+ this .node = node ;
61+ this .index = index ;
62+ }
63+ }
64+
5565 private static final boolean ENABLED = true ;
5666
5767 private final UIViewOperationQueue mUIViewOperationQueue ;
@@ -221,21 +231,39 @@ public void onBatchComplete() {
221231 mTagsWithLayoutVisited .clear ();
222232 }
223233
234+ private NodeIndexPair walkUpUntilNonLayoutOnly (
235+ ReactShadowNode node ,
236+ int indexInNativeChildren ) {
237+ while (node .isLayoutOnly ()) {
238+ ReactShadowNode parent = node .getParent ();
239+ if (parent == null ) {
240+ return null ;
241+ }
242+
243+ indexInNativeChildren = indexInNativeChildren + parent .getNativeOffsetForChild (node );
244+ node = parent ;
245+ }
246+
247+ return new NodeIndexPair (node , indexInNativeChildren );
248+ }
249+
224250 private void addNodeToNode (ReactShadowNode parent , ReactShadowNode child , int index ) {
225251 int indexInNativeChildren = parent .getNativeOffsetForChild (parent .getChildAt (index ));
226- boolean parentIsLayoutOnly = parent .isLayoutOnly ();
227- boolean childIsLayoutOnly = child .isLayoutOnly ();
228-
229- // Switch on the four cases of:
230- // add (layout-only|not layout-only) to (layout-only|not layout-only)
231- if (!parentIsLayoutOnly && !childIsLayoutOnly ) {
232- addNonLayoutNodeToNonLayoutNode (parent , child , indexInNativeChildren );
233- } else if (!childIsLayoutOnly ) {
234- addNonLayoutOnlyNodeToLayoutOnlyNode (parent , child , indexInNativeChildren );
235- } else if (!parentIsLayoutOnly ) {
236- addLayoutOnlyNodeToNonLayoutOnlyNode (parent , child , indexInNativeChildren );
252+ if (parent .isLayoutOnly ()) {
253+ NodeIndexPair result = walkUpUntilNonLayoutOnly (parent , indexInNativeChildren );
254+ if (result == null ) {
255+ // If the parent hasn't been attached to its native parent yet, don't issue commands to the
256+ // native hierarchy. We'll do that when the parent node actually gets attached somewhere.
257+ return ;
258+ }
259+ parent = result .node ;
260+ indexInNativeChildren = result .index ;
261+ }
262+
263+ if (!child .isLayoutOnly ()) {
264+ addNonLayoutNode (parent , child , indexInNativeChildren );
237265 } else {
238- addLayoutOnlyNodeToLayoutOnlyNode (parent , child , indexInNativeChildren );
266+ addLayoutOnlyNode (parent , child , indexInNativeChildren );
239267 }
240268 }
241269
@@ -262,73 +290,14 @@ private void removeNodeFromParent(ReactShadowNode nodeToRemove, boolean shouldDe
262290 }
263291 }
264292
265- private void addLayoutOnlyNodeToLayoutOnlyNode (
266- ReactShadowNode parent ,
267- ReactShadowNode child ,
268- int index ) {
269- ReactShadowNode parentParent = parent .getParent ();
270-
271- // If the parent hasn't been attached to its parent yet, don't issue commands to the native
272- // hierarchy. We'll do that when the parent node actually gets attached somewhere.
273- if (parentParent == null ) {
274- return ;
275- }
276-
277- int transformedIndex = index + parentParent .getNativeOffsetForChild (parent );
278- if (parentParent .isLayoutOnly ()) {
279- addLayoutOnlyNodeToLayoutOnlyNode (parentParent , child , transformedIndex );
280- } else {
281- addLayoutOnlyNodeToNonLayoutOnlyNode (parentParent , child , transformedIndex );
282- }
283- }
284-
285- private void addNonLayoutOnlyNodeToLayoutOnlyNode (
286- ReactShadowNode layoutOnlyNode ,
287- ReactShadowNode nonLayoutOnlyNode ,
288- int index ) {
289- ReactShadowNode parent = layoutOnlyNode .getParent ();
290-
291- // If the parent hasn't been attached to its parent yet, don't issue commands to the native
292- // hierarchy. We'll do that when the parent node actually gets attached somewhere.
293- if (parent == null ) {
294- return ;
295- }
296-
297- int transformedIndex = index + parent .getNativeOffsetForChild (layoutOnlyNode );
298- if (parent .isLayoutOnly ()) {
299- addNonLayoutOnlyNodeToLayoutOnlyNode (parent , nonLayoutOnlyNode , transformedIndex );
300- } else {
301- addNonLayoutNodeToNonLayoutNode (parent , nonLayoutOnlyNode , transformedIndex );
302- }
303- }
304-
305- private void addLayoutOnlyNodeToNonLayoutOnlyNode (
293+ private void addLayoutOnlyNode (
306294 ReactShadowNode nonLayoutOnlyNode ,
307295 ReactShadowNode layoutOnlyNode ,
308296 int index ) {
309- // Add all of the layout-only node's children to its parent instead
310- int currentIndex = index ;
311- for (int i = 0 ; i < layoutOnlyNode .getChildCount (); i ++) {
312- ReactShadowNode childToAdd = layoutOnlyNode .getChildAt (i );
313- Assertions .assertCondition (childToAdd .getNativeParent () == null );
314-
315- if (childToAdd .isLayoutOnly ()) {
316- // Adding this layout-only child could result in adding multiple native views
317- int childCountBefore = nonLayoutOnlyNode .getNativeChildCount ();
318- addLayoutOnlyNodeToNonLayoutOnlyNode (
319- nonLayoutOnlyNode ,
320- childToAdd ,
321- currentIndex );
322- int childCountAfter = nonLayoutOnlyNode .getNativeChildCount ();
323- currentIndex += childCountAfter - childCountBefore ;
324- } else {
325- addNonLayoutNodeToNonLayoutNode (nonLayoutOnlyNode , childToAdd , currentIndex );
326- currentIndex ++;
327- }
328- }
297+ addGrandchildren (nonLayoutOnlyNode , layoutOnlyNode , index );
329298 }
330299
331- private void addNonLayoutNodeToNonLayoutNode (
300+ private void addNonLayoutNode (
332301 ReactShadowNode parent ,
333302 ReactShadowNode child ,
334303 int index ) {
@@ -340,6 +309,31 @@ private void addNonLayoutNodeToNonLayoutNode(
340309 null );
341310 }
342311
312+ private void addGrandchildren (
313+ ReactShadowNode nativeParent ,
314+ ReactShadowNode child ,
315+ int index ) {
316+ Assertions .assertCondition (!nativeParent .isLayoutOnly ());
317+
318+ // `child` can't hold native children. Add all of `child`'s children to `parent`.
319+ int currentIndex = index ;
320+ for (int i = 0 ; i < child .getChildCount (); i ++) {
321+ ReactShadowNode grandchild = child .getChildAt (i );
322+ Assertions .assertCondition (grandchild .getNativeParent () == null );
323+
324+ if (grandchild .isLayoutOnly ()) {
325+ // Adding this child could result in adding multiple native views
326+ int grandchildCountBefore = nativeParent .getNativeChildCount ();
327+ addLayoutOnlyNode (nativeParent , grandchild , currentIndex );
328+ int grandchildCountAfter = nativeParent .getNativeChildCount ();
329+ currentIndex += grandchildCountAfter - grandchildCountBefore ;
330+ } else {
331+ addNonLayoutNode (nativeParent , grandchild , currentIndex );
332+ currentIndex ++;
333+ }
334+ }
335+ }
336+
343337 private void applyLayoutBase (ReactShadowNode node ) {
344338 int tag = node .getReactTag ();
345339 if (mTagsWithLayoutVisited .get (tag )) {
0 commit comments