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:
parent
7db444c9ae
commit
8de2acd3a9
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
/* package */ final class DrawView implements DrawCommand {
|
||||
|
||||
/* package */ static DrawView INSTANCE = new DrawView();
|
||||
|
||||
private DrawView() {}
|
||||
|
||||
@Override
|
||||
public void draw(FlatViewGroup parent, Canvas canvas) {
|
||||
parent.drawNextChild(canvas);
|
||||
}
|
||||
}
|
|
@ -23,12 +23,18 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
|||
* FlatNativeViewHierarchyManager is the only class that performs View manipulations. All of this
|
||||
* class methods can only be called from UI thread by {@link FlatUIViewOperationQueue}.
|
||||
*/
|
||||
/* package */ final class FlatNativeViewHierarchyManager extends NativeViewHierarchyManager {
|
||||
/* package */ final class FlatNativeViewHierarchyManager extends NativeViewHierarchyManager
|
||||
implements ViewResolver {
|
||||
|
||||
/* package */ FlatNativeViewHierarchyManager(ViewManagerRegistry viewManagers) {
|
||||
super(viewManagers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int reactTag) {
|
||||
return super.resolveView(reactTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootView(
|
||||
int tag,
|
||||
|
@ -60,6 +66,11 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
|||
}
|
||||
}
|
||||
|
||||
/* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
|
||||
view.mountViews(this, viewsToAdd, viewsToDetach);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates View bounds, possibly re-measuring and re-layouting it if the size changed.
|
||||
*
|
||||
|
@ -85,4 +96,11 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
|||
view.offsetTopAndBottom(top - view.getTop());
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void detachAllChildrenFromViews(int[] viewsToDetachAllChildrenFrom) {
|
||||
for (int viewTag : viewsToDetachAllChildrenFrom) {
|
||||
FlatViewGroup viewGroup = (FlatViewGroup) resolveView(viewTag);
|
||||
viewGroup.detachAllViewsFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,35 +14,8 @@ package com.facebook.react.flat;
|
|||
*/
|
||||
/* package */ final class FlatRootShadowNode extends FlatShadowNode {
|
||||
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||
|
||||
private int mViewLeft;
|
||||
private int mViewTop;
|
||||
private int mViewRight;
|
||||
private int mViewBottom;
|
||||
private boolean mIsUpdated;
|
||||
|
||||
@Override
|
||||
public int getScreenX() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenY() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenWidth() {
|
||||
return mViewRight - mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenHeight() {
|
||||
return mViewBottom - mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when this CSSNode tree needs to be re-laid out. If true, FlatUIImplementation
|
||||
* will request LayoutEngine to perform a layout pass to update node boundaries. This is used
|
||||
|
@ -66,72 +39,4 @@ package com.facebook.react.flat;
|
|||
/* package */ void markUpdated(boolean isUpdated) {
|
||||
mIsUpdated = isUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of DrawCommands to perform during the View's draw pass.
|
||||
*/
|
||||
/* package */ DrawCommand[] getDrawCommands() {
|
||||
return mDrawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of DrawCommands to perform during the View's draw pass. StateBuilder uses old
|
||||
* draw commands to compare to new draw commands and see if the View neds to be redrawn.
|
||||
*/
|
||||
/* package */ void setDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of AttachDetachListeners to call onAttach/onDetach when they are attached to or
|
||||
* detached from a View that this shadow node maps to.
|
||||
*/
|
||||
/* package */ void setAttachDetachListeners(AttachDetachListener[] listeners) {
|
||||
mAttachDetachListeners = listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of AttachDetachListeners associated with this shadow node.
|
||||
*/
|
||||
/* package */ AttachDetachListener[] getAttachDetachListeners() {
|
||||
return mAttachDetachListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets boundaries of the View that this node maps to relative to the parent left/top coordinate.
|
||||
*/
|
||||
/* package */ void setViewBounds(int left, int top, int right, int bottom) {
|
||||
mViewLeft = left;
|
||||
mViewTop = top;
|
||||
mViewRight = right;
|
||||
mViewBottom = bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewLeft() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewTop() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Right position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewRight() {
|
||||
return mViewRight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bottom position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewBottom() {
|
||||
return mViewBottom;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,18 @@ import com.facebook.react.uimanager.ViewProps;
|
|||
*/
|
||||
/* package */ class FlatShadowNode extends LayoutShadowNode {
|
||||
|
||||
/* package */ static final FlatShadowNode[] EMPTY_ARRAY = new FlatShadowNode[0];
|
||||
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||
private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY;
|
||||
private int mNativeParentTag;
|
||||
private int mViewLeft;
|
||||
private int mViewTop;
|
||||
private int mViewRight;
|
||||
private int mViewBottom;
|
||||
private boolean mMountsToView;
|
||||
private boolean mBackingViewIsCreated;
|
||||
private @Nullable DrawBackgroundColor mDrawBackground;
|
||||
|
||||
/**
|
||||
|
@ -48,14 +60,137 @@ import com.facebook.react.uimanager.ViewProps;
|
|||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getScreenX() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getScreenY() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getScreenWidth() {
|
||||
return mViewRight - mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getScreenHeight() {
|
||||
return mViewBottom - mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks root node as updated to trigger a StateBuilder pass to collect DrawCommands for the node
|
||||
* tree. Use it when FlatShadowNode is updated but doesn't require a layout pass (e.g. background
|
||||
* color is changed).
|
||||
*/
|
||||
protected final void invalidate() {
|
||||
// getRootNode() returns an ReactShadowNode, which is guarantied to be a FlatRootShadowNode.
|
||||
FlatRootShadowNode rootNode = (FlatRootShadowNode) getRootNode();
|
||||
rootNode.markUpdated(true);
|
||||
((FlatRootShadowNode) getRootNode()).markUpdated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of DrawCommands to perform during the View's draw pass.
|
||||
*/
|
||||
/* package */ DrawCommand[] getDrawCommands() {
|
||||
return mDrawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of DrawCommands to perform during the View's draw pass. StateBuilder uses old
|
||||
* draw commands to compare to new draw commands and see if the View neds to be redrawn.
|
||||
*/
|
||||
/* package */ void setDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of AttachDetachListeners to call onAttach/onDetach when they are attached to or
|
||||
* detached from a View that this shadow node maps to.
|
||||
*/
|
||||
/* package */ void setAttachDetachListeners(AttachDetachListener[] listeners) {
|
||||
mAttachDetachListeners = listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of AttachDetachListeners associated with this shadow node.
|
||||
*/
|
||||
/* package */ AttachDetachListener[] getAttachDetachListeners() {
|
||||
return mAttachDetachListeners;
|
||||
}
|
||||
|
||||
/* package */ final FlatShadowNode[] getNativeChildren() {
|
||||
return mNativeChildren;
|
||||
}
|
||||
|
||||
/* package */ final void setNativeChildren(FlatShadowNode[] nativeChildren) {
|
||||
mNativeChildren = nativeChildren;
|
||||
}
|
||||
|
||||
/* package */ final int getNativeParentTag() {
|
||||
return mNativeParentTag;
|
||||
}
|
||||
|
||||
/* package */ final void setNativeParentTag(int nativeParentTag) {
|
||||
mNativeParentTag = nativeParentTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets boundaries of the View that this node maps to relative to the parent left/top coordinate.
|
||||
*/
|
||||
/* package */ void setViewBounds(int left, int top, int right, int bottom) {
|
||||
mViewLeft = left;
|
||||
mViewTop = top;
|
||||
mViewRight = right;
|
||||
mViewBottom = bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewLeft() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewTop() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Right position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewRight() {
|
||||
return mViewRight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bottom position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewBottom() {
|
||||
return mViewBottom;
|
||||
}
|
||||
|
||||
/* package */ final void forceMountToView() {
|
||||
if (!mMountsToView) {
|
||||
mMountsToView = true;
|
||||
if (getParent() != null) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ final boolean mountsToView() {
|
||||
return mMountsToView;
|
||||
}
|
||||
|
||||
/* package */ final boolean isBackingViewCreated() {
|
||||
return mBackingViewIsCreated;
|
||||
}
|
||||
|
||||
/* package */ final void signalBackingViewIsCreated() {
|
||||
mBackingViewIsCreated = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,24 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||
}
|
||||
}
|
||||
|
||||
private final class UpdateViewGroup implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int[] mViewsToAdd;
|
||||
private final int[] mViewsToDetach;
|
||||
|
||||
private UpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
mReactTag = reactTag;
|
||||
mViewsToAdd = viewsToAdd;
|
||||
mViewsToDetach = viewsToDetach;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.updateViewGroup(mReactTag, mViewsToAdd, mViewsToDetach);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIOperation that updates View bounds for a View defined by reactTag.
|
||||
*/
|
||||
|
@ -74,6 +92,19 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||
}
|
||||
}
|
||||
|
||||
public final class DetachAllChildrenFromViews implements UIViewOperationQueue.UIOperation {
|
||||
private @Nullable int[] mViewsToDetachAllChildrenFrom;
|
||||
|
||||
public void setViewsToDetachAllChildrenFrom(int[] viewsToDetachAllChildrenFrom) {
|
||||
mViewsToDetachAllChildrenFrom = viewsToDetachAllChildrenFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.detachAllChildrenFromViews(mViewsToDetachAllChildrenFrom);
|
||||
}
|
||||
}
|
||||
|
||||
public FlatUIViewOperationQueue(
|
||||
ReactApplicationContext reactContext,
|
||||
FlatNativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
|
@ -92,10 +123,20 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||
enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners));
|
||||
}
|
||||
|
||||
public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
enqueueUIOperation(new UpdateViewGroup(reactTag, viewsToAdd, viewsToDetach));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a new UIOperation that will update View bounds for a View defined by reactTag.
|
||||
*/
|
||||
public void enqueueUpdateViewBounds(int reactTag, int left, int top, int right, int bottom) {
|
||||
enqueueUIOperation(new UpdateViewBounds(reactTag, left, top, right, bottom));
|
||||
}
|
||||
|
||||
public DetachAllChildrenFromViews enqueueDetachAllChildrenFromViews() {
|
||||
DetachAllChildrenFromViews op = new DetachAllChildrenFromViews();
|
||||
enqueueUIOperation(op);
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ import javax.annotation.Nullable;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
/**
|
||||
* A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over
|
||||
|
@ -45,12 +47,18 @@ import android.view.ViewGroup;
|
|||
private @Nullable InvalidateCallback mInvalidateCallback;
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||
private int mDrawChildIndex = 0;
|
||||
private boolean mIsAttached = false;
|
||||
|
||||
/* package */ FlatViewGroup(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void detachAllViewsFromParent() {
|
||||
super.detachAllViewsFromParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
@ -58,6 +66,19 @@ import android.view.ViewGroup;
|
|||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
drawCommand.draw(this, canvas);
|
||||
}
|
||||
|
||||
if (mDrawChildIndex != getChildCount()) {
|
||||
throw new RuntimeException(
|
||||
"Did not draw all children: " + mDrawChildIndex + " / " + getChildCount());
|
||||
}
|
||||
mDrawChildIndex = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
// suppress
|
||||
// no drawing -> no invalidate -> return false
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,6 +111,13 @@ import android.view.ViewGroup;
|
|||
dispatchOnDetached(mAttachDetachListeners);
|
||||
}
|
||||
|
||||
/* package */ void drawNextChild(Canvas canvas) {
|
||||
View child = getChildAt(mDrawChildIndex);
|
||||
super.drawChild(canvas, child, getDrawingTime());
|
||||
|
||||
++mDrawChildIndex;
|
||||
}
|
||||
|
||||
/* package */ void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
invalidate();
|
||||
|
@ -115,6 +143,32 @@ import android.view.ViewGroup;
|
|||
mAttachDetachListeners = listeners;
|
||||
}
|
||||
|
||||
/* package */ void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
for (int viewToAdd : viewsToAdd) {
|
||||
if (viewToAdd > 0) {
|
||||
View view = ensureViewHasNoParent(viewResolver.getView(viewToAdd));
|
||||
addView(view, -1, ensureLayoutParams(view.getLayoutParams()));
|
||||
} else {
|
||||
View view = ensureViewHasNoParent(viewResolver.getView(-viewToAdd));
|
||||
attachViewToParent(view, -1, ensureLayoutParams(view.getLayoutParams()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int viewToDetach : viewsToDetach) {
|
||||
removeDetachedView(viewResolver.getView(viewToDetach), false);
|
||||
}
|
||||
}
|
||||
|
||||
private View ensureViewHasNoParent(View view) {
|
||||
ViewParent oldParent = view.getParent();
|
||||
if (oldParent != null) {
|
||||
throw new RuntimeException(
|
||||
"Cannot add view " + view + " to " + this + " while it has a parent " + oldParent);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void dispatchOnAttached(AttachDetachListener[] listeners) {
|
||||
int numListeners = listeners.length;
|
||||
if (numListeners == 0) {
|
||||
|
@ -139,4 +193,11 @@ import android.view.ViewGroup;
|
|||
listener.onDetached();
|
||||
}
|
||||
}
|
||||
|
||||
private ViewGroup.LayoutParams ensureLayoutParams(ViewGroup.LayoutParams lp) {
|
||||
if (checkLayoutParams(lp)) {
|
||||
return lp;
|
||||
}
|
||||
return generateDefaultLayoutParams();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
abstract class FlatViewManager<C extends FlatShadowNode> extends ViewManager<FlatViewGroup, C> {
|
||||
|
||||
@Override
|
||||
protected FlatViewGroup createViewInstance(ThemedReactContext reactContext) {
|
||||
return new FlatViewGroup(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExtraData(FlatViewGroup root, Object extraData) {
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
/* package */ final class RCTImageViewManager extends VirtualViewManager<RCTImageView> {
|
||||
/* package */ final class RCTImageViewManager extends FlatViewManager<RCTImageView> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
|
|
@ -12,7 +12,7 @@ package com.facebook.react.flat;
|
|||
/**
|
||||
* ViewManager that creates instances of RCTText.
|
||||
*/
|
||||
/* package */ final class RCTTextManager extends VirtualViewManager<RCTText> {
|
||||
/* package */ final class RCTTextManager extends FlatViewManager<RCTText> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
|
|
@ -12,7 +12,7 @@ package com.facebook.react.flat;
|
|||
/**
|
||||
* ViewManager that creates instances of RCTView.
|
||||
*/
|
||||
/* package */ final class RCTViewManager extends VirtualViewManager<RCTView> {
|
||||
/* package */ final class RCTViewManager extends FlatViewManager<RCTView> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface ViewResolver {
|
||||
public View getView(int tag);
|
||||
}
|
Loading…
Reference in New Issue