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

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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) {
}
}

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

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;
}
}

View File

@ -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);
}