Allow FlatShadowNode mouting to its own view

Summary: @public This diff adds a `FlatShadowNode.forceMountToView()` method that will render its contents in it own `View`.

Reviewed By: sriramramani

Differential Revision: D2564502
This commit is contained in:
Denis Koroskin
2015-12-13 15:54:52 -08:00
committed by Ahmed El-Helw
parent 7db444c9ae
commit 8de2acd3a9
12 changed files with 472 additions and 118 deletions

View File

@@ -9,6 +9,12 @@
package com.facebook.react.flat;
import java.util.ArrayList;
import javax.annotation.Nullable;
import com.facebook.react.uimanager.CatalystStylesDiffMap;
/**
* Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should
* be from JavaScript perspective. StateBuilder is a helper class that can walk the shadow node tree
@@ -17,12 +23,21 @@ package com.facebook.react.flat;
*/
/* package */ final class StateBuilder {
private static final int[] EMPTY_INT_ARRAY = new int[0];
private final FlatUIViewOperationQueue mOperationsQueue;
private final ElementsList<DrawCommand> mDrawCommands =
new ElementsList<>(DrawCommand.EMPTY_ARRAY);
private final ElementsList<AttachDetachListener> mAttachDetachListeners =
new ElementsList<>(AttachDetachListener.EMPTY_ARRAY);
private final ElementsList<FlatShadowNode> mNativeChildren =
new ElementsList<>(FlatShadowNode.EMPTY_ARRAY);
private final ArrayList<FlatShadowNode> mViewsToDetachAllChildrenFrom = new ArrayList<>();
private final ArrayList<FlatShadowNode> mViewsToDetach = new ArrayList<>();
private @Nullable FlatUIViewOperationQueue.DetachAllChildrenFromViews mDetachAllChildrenFromViews;
/* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) {
mOperationsQueue = operationsQueue;
@@ -32,8 +47,24 @@ package com.facebook.react.flat;
* Given a root of the laid-out shadow node hierarchy, walks the tree and generates an array of
* DrawCommands that will then mount in UI thread to a root FlatViewGroup so that it can draw.
*/
/* package*/ void applyUpdates(FlatRootShadowNode node) {
collectStateAndUpdateViewBounds(node, 0, 0);
/* package */ void applyUpdates(FlatShadowNode node) {
int tag = node.getReactTag();
float width = node.getLayoutWidth();
float height = node.getLayoutHeight();
collectStateForMountableNode(node, tag, width, height);
float left = node.getLayoutX();
float top = node.getLayoutY();
updateViewBounds(node, tag, left, top, left + width, top + height);
if (mDetachAllChildrenFromViews != null) {
int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom);
mViewsToDetachAllChildrenFrom.clear();
mDetachAllChildrenFromViews.setViewsToDetachAllChildrenFrom(viewsToDetachAllChildrenFrom);
mDetachAllChildrenFromViews = null;
}
}
/**
@@ -47,11 +78,27 @@ package com.facebook.react.flat;
mAttachDetachListeners.add(listener);
}
/* package */ void ensureBackingViewIsCreated(
FlatShadowNode node,
int tag,
@Nullable CatalystStylesDiffMap styles) {
if (node.isBackingViewCreated()) {
return;
}
mOperationsQueue.enqueueCreateView(node.getThemedContext(), tag, node.getViewClass(), styles);
node.signalBackingViewIsCreated();
}
private void addNativeChild(FlatShadowNode nativeChild) {
mNativeChildren.add(nativeChild);
}
/**
* Updates boundaries of a View that a give nodes maps to.
*/
private void updateViewBounds(
FlatRootShadowNode node,
FlatShadowNode node,
int tag,
float leftInParent,
float topInParent,
@@ -77,12 +124,13 @@ package com.facebook.react.flat;
* Collects state (DrawCommands) for a given node that will mount to a View.
*/
private void collectStateForMountableNode(
FlatRootShadowNode node,
FlatShadowNode node,
int tag,
float width,
float height) {
mDrawCommands.start(node.getDrawCommands());
mAttachDetachListeners.start(node.getAttachDetachListeners());
mNativeChildren.start(node.getNativeChildren());
collectStateRecursively(node, 0, 0, width, height);
@@ -100,8 +148,72 @@ package com.facebook.react.flat;
}
if (shouldUpdateMountState) {
mOperationsQueue.enqueueUpdateMountState(tag, drawCommands, listeners);
mOperationsQueue.enqueueUpdateMountState(
tag,
drawCommands,
listeners);
}
final FlatShadowNode[] nativeChildren = mNativeChildren.finish();
if (nativeChildren != null) {
updateNativeChildren(node, tag, node.getNativeChildren(), nativeChildren);
}
}
private void updateNativeChildren(
FlatShadowNode node,
int tag,
FlatShadowNode[] oldNativeChildren,
FlatShadowNode[] newNativeChildren) {
node.setNativeChildren(newNativeChildren);
if (mDetachAllChildrenFromViews == null) {
mDetachAllChildrenFromViews = mOperationsQueue.enqueueDetachAllChildrenFromViews();
}
if (oldNativeChildren.length != 0) {
mViewsToDetachAllChildrenFrom.add(node);
}
int numViewsToAdd = newNativeChildren.length;
final int[] viewsToAdd;
if (numViewsToAdd == 0) {
viewsToAdd = EMPTY_INT_ARRAY;
} else {
viewsToAdd = new int[numViewsToAdd];
int i = 0;
for (FlatShadowNode child : newNativeChildren) {
if (child.getNativeParentTag() == tag) {
viewsToAdd[i] = -child.getReactTag();
} else {
viewsToAdd[i] = child.getReactTag();
}
// all views we add are first start detached
child.setNativeParentTag(-1);
++i;
}
}
// Populate an array of views to detach.
// These views still have their native parent set as opposed to being reset to -1
for (FlatShadowNode child : oldNativeChildren) {
if (child.getNativeParentTag() == tag) {
// View is attached to old parent and needs to be removed.
mViewsToDetach.add(child);
child.setNativeParentTag(-1);
}
}
final int[] viewsToDetach = collectViewTags(mViewsToDetach);
mViewsToDetach.clear();
// restore correct parent tag
for (FlatShadowNode child : newNativeChildren) {
child.setNativeParentTag(tag);
}
mOperationsQueue.enqueueUpdateViewGroup(tag, viewsToAdd, viewsToDetach);
}
/**
@@ -121,20 +233,15 @@ package com.facebook.react.flat;
for (int i = 0, childCount = node.getChildCount(); i != childCount; ++i) {
FlatShadowNode child = (FlatShadowNode) node.getChildAt(i);
float childLeft = left + child.getLayoutX();
float childTop = top + child.getLayoutY();
float childRight = childLeft + child.getLayoutWidth();
float childBottom = childTop + child.getLayoutHeight();
collectStateRecursively(child, childLeft, childTop, childRight, childBottom);
processNodeAndCollectState(child, left, top);
}
}
/**
* Collects state and updates View boundaries for a given root node.
* Collects state and updates View boundaries for a given node tree.
*/
private void collectStateAndUpdateViewBounds(
FlatRootShadowNode node,
private void processNodeAndCollectState(
FlatShadowNode node,
float parentLeft,
float parentTop) {
int tag = node.getReactTag();
@@ -147,8 +254,30 @@ package com.facebook.react.flat;
float right = left + width;
float bottom = top + height;
collectStateForMountableNode(node, tag, width, height);
if (node.mountsToView()) {
ensureBackingViewIsCreated(node, tag, null);
updateViewBounds(node, tag, left, top, right, bottom);
addNativeChild(node);
mDrawCommands.add(DrawView.INSTANCE);
collectStateForMountableNode(node, tag, width, height);
updateViewBounds(node, tag, left, top, right, bottom);
} else {
collectStateRecursively(node, left, top, right, bottom);
}
}
private static int[] collectViewTags(ArrayList<FlatShadowNode> views) {
int numViews = views.size();
if (numViews == 0) {
return EMPTY_INT_ARRAY;
}
int[] viewTags = new int[numViews];
for (int i = 0; i < numViews; ++i) {
viewTags[i] = views.get(i).getReactTag();
}
return viewTags;
}
}