diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java index abf558948ac..d6c80e423b4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatNativeViewHierarchyManager.java @@ -56,7 +56,8 @@ import com.facebook.react.uimanager.ViewManagerRegistry; /* package */ void updateMountState( int reactTag, @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners) { + @Nullable AttachDetachListener[] listeners, + @Nullable NodeRegion[] nodeRegions) { FlatViewGroup view = (FlatViewGroup) resolveView(reactTag); if (drawCommands != null) { view.mountDrawCommands(drawCommands); @@ -64,6 +65,9 @@ import com.facebook.react.uimanager.ViewManagerRegistry; if (listeners != null) { view.mountAttachDetachListeners(listeners); } + if (nodeRegions != null) { + view.mountNodeRegions(nodeRegions); + } } /* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java index dc737fd1a49..b8b261d5d0f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatShadowNode.java @@ -25,7 +25,9 @@ import com.facebook.react.uimanager.ViewProps; private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY; + private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY; + private NodeRegion mNodeRegion = NodeRegion.EMPTY; private int mNativeParentTag; private int mViewLeft; private int mViewTop; @@ -135,6 +137,22 @@ import com.facebook.react.uimanager.ViewProps; mNativeParentTag = nativeParentTag; } + /* package */ final NodeRegion[] getNodeRegions() { + return mNodeRegions; + } + + /* package */ final void setNodeRegions(NodeRegion[] nodeRegion) { + mNodeRegions = nodeRegion; + } + + /* package */ final NodeRegion getNodeRegion() { + return mNodeRegion; + } + + /* package */ final void setNodeRegion(NodeRegion nodeRegion) { + mNodeRegion = nodeRegion; + } + /** * Sets boundaries of the View that this node maps to relative to the parent left/top coordinate. */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java index e9106ddeb88..8d12561ee95 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIViewOperationQueue.java @@ -30,14 +30,17 @@ import com.facebook.react.uimanager.UIViewOperationQueue; private final int mReactTag; private final @Nullable DrawCommand[] mDrawCommands; private final @Nullable AttachDetachListener[] mAttachDetachListeners; + private final @Nullable NodeRegion[] mNodeRegions; private UpdateMountState( int reactTag, @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners) { + @Nullable AttachDetachListener[] listeners, + @Nullable NodeRegion[] nodeRegions) { mReactTag = reactTag; mDrawCommands = drawCommands; mAttachDetachListeners = listeners; + mNodeRegions = nodeRegions; } @Override @@ -45,7 +48,8 @@ import com.facebook.react.uimanager.UIViewOperationQueue; mNativeViewHierarchyManager.updateMountState( mReactTag, mDrawCommands, - mAttachDetachListeners); + mAttachDetachListeners, + mNodeRegions); } } @@ -119,8 +123,9 @@ import com.facebook.react.uimanager.UIViewOperationQueue; public void enqueueUpdateMountState( int reactTag, @Nullable DrawCommand[] drawCommands, - @Nullable AttachDetachListener[] listeners) { - enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners)); + @Nullable AttachDetachListener[] listeners, + @Nullable NodeRegion[] nodeRegions) { + enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners, nodeRegions)); } public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java index 1c11ee18e8c..feb44693598 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java @@ -19,11 +19,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import com.facebook.react.uimanager.ReactCompoundView; + /** * A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over * array of DrawCommands, executing them one by one. */ -/* package */ final class FlatViewGroup extends ViewGroup { +/* package */ final class FlatViewGroup extends ViewGroup implements ReactCompoundView { /** * Helper class that allows AttachDetachListener to invalidate the hosting View. */ @@ -47,6 +49,7 @@ import android.view.ViewParent; private @Nullable InvalidateCallback mInvalidateCallback; private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY; private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY; + private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY; private int mDrawChildIndex = 0; private boolean mIsAttached = false; @@ -59,6 +62,19 @@ import android.view.ViewParent; super.detachAllViewsFromParent(); } + @Override + public int reactTagForTouch(float touchX, float touchY) { + for (NodeRegion nodeRegion : mNodeRegions) { + if (nodeRegion.mLeft <= touchX && touchX < nodeRegion.mRight && + nodeRegion.mTop <= touchY && touchY < nodeRegion.mBottom) { + return nodeRegion.mTag; + } + } + + // no children found + return getId(); + } + @Override public void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -143,6 +159,10 @@ import android.view.ViewParent; mAttachDetachListeners = listeners; } + /* package */ void mountNodeRegions(NodeRegion[] nodeRegions) { + mNodeRegions = nodeRegions; + } + /* package */ void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) { for (int viewToAdd : viewsToAdd) { if (viewToAdd > 0) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java b/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java new file mode 100644 index 00000000000..6ea1a1abc77 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/NodeRegion.java @@ -0,0 +1,29 @@ +/** + * 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; + +/* package */ final class NodeRegion { + /* package */ static final NodeRegion[] EMPTY_ARRAY = new NodeRegion[0]; + /* package */ static final NodeRegion EMPTY = new NodeRegion(0, 0, 0, 0, -1); + + /* package */ final float mLeft; + /* package */ final float mTop; + /* package */ final float mRight; + /* package */ final float mBottom; + /* package */ final int mTag; + + /* package */ NodeRegion(float left, float top, float right, float bottom, int tag) { + mLeft = left; + mTop = top; + mRight = right; + mBottom = bottom; + mTag = tag; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java index c59401da576..c3b48b8cc39 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java @@ -31,6 +31,8 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; new ElementsList<>(DrawCommand.EMPTY_ARRAY); private final ElementsList mAttachDetachListeners = new ElementsList<>(AttachDetachListener.EMPTY_ARRAY); + private final ElementsList mNodeRegions = + new ElementsList<>(NodeRegion.EMPTY_ARRAY); private final ElementsList mNativeChildren = new ElementsList<>(FlatShadowNode.EMPTY_ARRAY); @@ -56,7 +58,10 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; float left = node.getLayoutX(); float top = node.getLayoutY(); - updateViewBounds(node, tag, left, top, left + width, top + height); + float right = left + width; + float bottom = top + height; + updateNodeRegion(node, tag, left, top, right, bottom); + updateViewBounds(node, tag, left, top, right, bottom); if (mDetachAllChildrenFromViews != null) { int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom); @@ -90,6 +95,10 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; node.signalBackingViewIsCreated(); } + private void addNodeRegion(NodeRegion nodeRegion) { + mNodeRegions.add(nodeRegion); + } + private void addNativeChild(FlatShadowNode nativeChild) { mNativeChildren.add(nativeChild); } @@ -130,6 +139,7 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; float height) { mDrawCommands.start(node.getDrawCommands()); mAttachDetachListeners.start(node.getAttachDetachListeners()); + mNodeRegions.start(node.getNodeRegions()); mNativeChildren.start(node.getNativeChildren()); collectStateRecursively(node, 0, 0, width, height); @@ -147,11 +157,18 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; node.setAttachDetachListeners(listeners); } + final NodeRegion[] nodeRegions = mNodeRegions.finish(); + if (nodeRegions != null) { + shouldUpdateMountState = true; + node.setNodeRegions(nodeRegions); + } + if (shouldUpdateMountState) { mOperationsQueue.enqueueUpdateMountState( tag, drawCommands, - listeners); + listeners, + nodeRegions); } final FlatShadowNode[] nativeChildren = mNativeChildren.finish(); @@ -254,6 +271,8 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; float right = left + width; float bottom = top + height; + updateNodeRegion(node, tag, left, top, right, bottom); + if (node.mountsToView()) { ensureBackingViewIsCreated(node, tag, null); @@ -264,6 +283,21 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap; updateViewBounds(node, tag, left, top, right, bottom); } else { collectStateRecursively(node, left, top, right, bottom); + addNodeRegion(node.getNodeRegion()); + } + } + + private static void updateNodeRegion( + FlatShadowNode node, + int tag, + float left, + float top, + float right, + float bottom) { + final NodeRegion nodeRegion = node.getNodeRegion(); + if (nodeRegion.mLeft != left || nodeRegion.mTop != top || + nodeRegion.mRight != right || nodeRegion.mBottom != bottom) { + node.setNodeRegion(new NodeRegion(left, top, right, bottom, tag)); } }