Android: Refactor HierarchyOptimizer in preparation of inline view support

Summary:
This PR was split from commits originally in #8619. /cc dmmiller

These refactorings to the HierarchyOptimizer are in preparation for implementing support for inline views in #8619.

**Refactoring 1: Collapse add*LayoutOnlyNodeToLayoutOnlyNode**

addLayoutOnlyNodeToLayoutOnlyNode and addNonLayoutOnlyNodeToLayoutOnlyNode
had nearly identical implementations. They both walk thru the ancestors
looking for a nonlayout-only node and adjusting the passed in index at each
step. This introduces a new function, walkUpUntilNonLayoutOnly, which
takes care of that responsibility. This simplifies addNodeToNode
because it can now consider the type of the parent and the type of
the child independently.

**Refactoring 2: Extract addGrandchildren**

Pull out addLayoutOnlyNode's logic into a helper called
addGrandchildren. We will need to call this method in
another place later.

**Test plan (required)**

This change was tested with UIExplorer and a small test app and it's being used in my team's app.
Closes https://github.com/facebook/react-native/pull/8908

Differential Revision: D3592783

Pulled By: dmmiller

fbshipit-source-id: a513e8d381e71112ce6348bbee7d4a7c62c33619
This commit is contained in:
Adam Comella 2016-07-20 04:23:24 -07:00 committed by Facebook Github Bot 0
parent 0587c85094
commit e1b3bbdb04

View File

@ -52,6 +52,16 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator;
*/ */
public class NativeViewHierarchyOptimizer { public class NativeViewHierarchyOptimizer {
private static class NodeIndexPair {
public final ReactShadowNode node;
public final int index;
NodeIndexPair(ReactShadowNode node, int index) {
this.node = node;
this.index = index;
}
}
private static final boolean ENABLED = true; private static final boolean ENABLED = true;
private final UIViewOperationQueue mUIViewOperationQueue; private final UIViewOperationQueue mUIViewOperationQueue;
@ -221,21 +231,39 @@ public class NativeViewHierarchyOptimizer {
mTagsWithLayoutVisited.clear(); mTagsWithLayoutVisited.clear();
} }
private NodeIndexPair walkUpUntilNonLayoutOnly(
ReactShadowNode node,
int indexInNativeChildren) {
while (node.isLayoutOnly()) {
ReactShadowNode parent = node.getParent();
if (parent == null) {
return null;
}
indexInNativeChildren = indexInNativeChildren + parent.getNativeOffsetForChild(node);
node = parent;
}
return new NodeIndexPair(node, indexInNativeChildren);
}
private void addNodeToNode(ReactShadowNode parent, ReactShadowNode child, int index) { private void addNodeToNode(ReactShadowNode parent, ReactShadowNode child, int index) {
int indexInNativeChildren = parent.getNativeOffsetForChild(parent.getChildAt(index)); int indexInNativeChildren = parent.getNativeOffsetForChild(parent.getChildAt(index));
boolean parentIsLayoutOnly = parent.isLayoutOnly(); if (parent.isLayoutOnly()) {
boolean childIsLayoutOnly = child.isLayoutOnly(); NodeIndexPair result = walkUpUntilNonLayoutOnly(parent, indexInNativeChildren);
if (result == null) {
// If the parent hasn't been attached to its native parent yet, don't issue commands to the
// native hierarchy. We'll do that when the parent node actually gets attached somewhere.
return;
}
parent = result.node;
indexInNativeChildren = result.index;
}
// Switch on the four cases of: if (!child.isLayoutOnly()) {
// add (layout-only|not layout-only) to (layout-only|not layout-only) addNonLayoutNode(parent, child, indexInNativeChildren);
if (!parentIsLayoutOnly && !childIsLayoutOnly) {
addNonLayoutNodeToNonLayoutNode(parent, child, indexInNativeChildren);
} else if (!childIsLayoutOnly) {
addNonLayoutOnlyNodeToLayoutOnlyNode(parent, child, indexInNativeChildren);
} else if (!parentIsLayoutOnly) {
addLayoutOnlyNodeToNonLayoutOnlyNode(parent, child, indexInNativeChildren);
} else { } else {
addLayoutOnlyNodeToLayoutOnlyNode(parent, child, indexInNativeChildren); addLayoutOnlyNode(parent, child, indexInNativeChildren);
} }
} }
@ -262,73 +290,14 @@ public class NativeViewHierarchyOptimizer {
} }
} }
private void addLayoutOnlyNodeToLayoutOnlyNode( private void addLayoutOnlyNode(
ReactShadowNode parent,
ReactShadowNode child,
int index) {
ReactShadowNode parentParent = parent.getParent();
// If the parent hasn't been attached to its parent yet, don't issue commands to the native
// hierarchy. We'll do that when the parent node actually gets attached somewhere.
if (parentParent == null) {
return;
}
int transformedIndex = index + parentParent.getNativeOffsetForChild(parent);
if (parentParent.isLayoutOnly()) {
addLayoutOnlyNodeToLayoutOnlyNode(parentParent, child, transformedIndex);
} else {
addLayoutOnlyNodeToNonLayoutOnlyNode(parentParent, child, transformedIndex);
}
}
private void addNonLayoutOnlyNodeToLayoutOnlyNode(
ReactShadowNode layoutOnlyNode,
ReactShadowNode nonLayoutOnlyNode,
int index) {
ReactShadowNode parent = layoutOnlyNode.getParent();
// If the parent hasn't been attached to its parent yet, don't issue commands to the native
// hierarchy. We'll do that when the parent node actually gets attached somewhere.
if (parent == null) {
return;
}
int transformedIndex = index + parent.getNativeOffsetForChild(layoutOnlyNode);
if (parent.isLayoutOnly()) {
addNonLayoutOnlyNodeToLayoutOnlyNode(parent, nonLayoutOnlyNode, transformedIndex);
} else {
addNonLayoutNodeToNonLayoutNode(parent, nonLayoutOnlyNode, transformedIndex);
}
}
private void addLayoutOnlyNodeToNonLayoutOnlyNode(
ReactShadowNode nonLayoutOnlyNode, ReactShadowNode nonLayoutOnlyNode,
ReactShadowNode layoutOnlyNode, ReactShadowNode layoutOnlyNode,
int index) { int index) {
// Add all of the layout-only node's children to its parent instead addGrandchildren(nonLayoutOnlyNode, layoutOnlyNode, index);
int currentIndex = index;
for (int i = 0; i < layoutOnlyNode.getChildCount(); i++) {
ReactShadowNode childToAdd = layoutOnlyNode.getChildAt(i);
Assertions.assertCondition(childToAdd.getNativeParent() == null);
if (childToAdd.isLayoutOnly()) {
// Adding this layout-only child could result in adding multiple native views
int childCountBefore = nonLayoutOnlyNode.getNativeChildCount();
addLayoutOnlyNodeToNonLayoutOnlyNode(
nonLayoutOnlyNode,
childToAdd,
currentIndex);
int childCountAfter = nonLayoutOnlyNode.getNativeChildCount();
currentIndex += childCountAfter - childCountBefore;
} else {
addNonLayoutNodeToNonLayoutNode(nonLayoutOnlyNode, childToAdd, currentIndex);
currentIndex++;
}
}
} }
private void addNonLayoutNodeToNonLayoutNode( private void addNonLayoutNode(
ReactShadowNode parent, ReactShadowNode parent,
ReactShadowNode child, ReactShadowNode child,
int index) { int index) {
@ -340,6 +309,31 @@ public class NativeViewHierarchyOptimizer {
null); null);
} }
private void addGrandchildren(
ReactShadowNode nativeParent,
ReactShadowNode child,
int index) {
Assertions.assertCondition(!nativeParent.isLayoutOnly());
// `child` can't hold native children. Add all of `child`'s children to `parent`.
int currentIndex = index;
for (int i = 0; i < child.getChildCount(); i++) {
ReactShadowNode grandchild = child.getChildAt(i);
Assertions.assertCondition(grandchild.getNativeParent() == null);
if (grandchild.isLayoutOnly()) {
// Adding this child could result in adding multiple native views
int grandchildCountBefore = nativeParent.getNativeChildCount();
addLayoutOnlyNode(nativeParent, grandchild, currentIndex);
int grandchildCountAfter = nativeParent.getNativeChildCount();
currentIndex += grandchildCountAfter - grandchildCountBefore;
} else {
addNonLayoutNode(nativeParent, grandchild, currentIndex);
currentIndex++;
}
}
}
private void applyLayoutBase(ReactShadowNode node) { private void applyLayoutBase(ReactShadowNode node) {
int tag = node.getReactTag(); int tag = node.getReactTag();
if (mTagsWithLayoutVisited.get(tag)) { if (mTagsWithLayoutVisited.get(tag)) {