Add NodeRegion to allow any FlatShadowNode to respond to touch events
Summary: @public When Android dispatches `MotionEvent` to `ReactRootView`, it needs to find a correspoding react node that should receive it. To be able to do it, we need to store boundaries of every `FlatShadowNode` in `FlatViewGroup`. Then we can iterate over node boundaries and find one that contains the touch event coordinates. Reviewed By: sriramramani Differential Revision: D2694197
This commit is contained in:
parent
8de2acd3a9
commit
dad378e394
|
@ -56,7 +56,8 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||||
/* package */ void updateMountState(
|
/* package */ void updateMountState(
|
||||||
int reactTag,
|
int reactTag,
|
||||||
@Nullable DrawCommand[] drawCommands,
|
@Nullable DrawCommand[] drawCommands,
|
||||||
@Nullable AttachDetachListener[] listeners) {
|
@Nullable AttachDetachListener[] listeners,
|
||||||
|
@Nullable NodeRegion[] nodeRegions) {
|
||||||
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
|
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
|
||||||
if (drawCommands != null) {
|
if (drawCommands != null) {
|
||||||
view.mountDrawCommands(drawCommands);
|
view.mountDrawCommands(drawCommands);
|
||||||
|
@ -64,6 +65,9 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||||
if (listeners != null) {
|
if (listeners != null) {
|
||||||
view.mountAttachDetachListeners(listeners);
|
view.mountAttachDetachListeners(listeners);
|
||||||
}
|
}
|
||||||
|
if (nodeRegions != null) {
|
||||||
|
view.mountNodeRegions(nodeRegions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
/* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||||
|
|
|
@ -25,7 +25,9 @@ import com.facebook.react.uimanager.ViewProps;
|
||||||
|
|
||||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||||
|
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
||||||
private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY;
|
private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY;
|
||||||
|
private NodeRegion mNodeRegion = NodeRegion.EMPTY;
|
||||||
private int mNativeParentTag;
|
private int mNativeParentTag;
|
||||||
private int mViewLeft;
|
private int mViewLeft;
|
||||||
private int mViewTop;
|
private int mViewTop;
|
||||||
|
@ -135,6 +137,22 @@ import com.facebook.react.uimanager.ViewProps;
|
||||||
mNativeParentTag = nativeParentTag;
|
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.
|
* Sets boundaries of the View that this node maps to relative to the parent left/top coordinate.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -30,14 +30,17 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||||
private final int mReactTag;
|
private final int mReactTag;
|
||||||
private final @Nullable DrawCommand[] mDrawCommands;
|
private final @Nullable DrawCommand[] mDrawCommands;
|
||||||
private final @Nullable AttachDetachListener[] mAttachDetachListeners;
|
private final @Nullable AttachDetachListener[] mAttachDetachListeners;
|
||||||
|
private final @Nullable NodeRegion[] mNodeRegions;
|
||||||
|
|
||||||
private UpdateMountState(
|
private UpdateMountState(
|
||||||
int reactTag,
|
int reactTag,
|
||||||
@Nullable DrawCommand[] drawCommands,
|
@Nullable DrawCommand[] drawCommands,
|
||||||
@Nullable AttachDetachListener[] listeners) {
|
@Nullable AttachDetachListener[] listeners,
|
||||||
|
@Nullable NodeRegion[] nodeRegions) {
|
||||||
mReactTag = reactTag;
|
mReactTag = reactTag;
|
||||||
mDrawCommands = drawCommands;
|
mDrawCommands = drawCommands;
|
||||||
mAttachDetachListeners = listeners;
|
mAttachDetachListeners = listeners;
|
||||||
|
mNodeRegions = nodeRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,7 +48,8 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||||
mNativeViewHierarchyManager.updateMountState(
|
mNativeViewHierarchyManager.updateMountState(
|
||||||
mReactTag,
|
mReactTag,
|
||||||
mDrawCommands,
|
mDrawCommands,
|
||||||
mAttachDetachListeners);
|
mAttachDetachListeners,
|
||||||
|
mNodeRegions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +123,9 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||||
public void enqueueUpdateMountState(
|
public void enqueueUpdateMountState(
|
||||||
int reactTag,
|
int reactTag,
|
||||||
@Nullable DrawCommand[] drawCommands,
|
@Nullable DrawCommand[] drawCommands,
|
||||||
@Nullable AttachDetachListener[] listeners) {
|
@Nullable AttachDetachListener[] listeners,
|
||||||
enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners));
|
@Nullable NodeRegion[] nodeRegions) {
|
||||||
|
enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners, nodeRegions));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||||
|
|
|
@ -19,11 +19,13 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
|
|
||||||
|
import com.facebook.react.uimanager.ReactCompoundView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over
|
* A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over
|
||||||
* array of DrawCommands, executing them one by one.
|
* 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.
|
* Helper class that allows AttachDetachListener to invalidate the hosting View.
|
||||||
*/
|
*/
|
||||||
|
@ -47,6 +49,7 @@ import android.view.ViewParent;
|
||||||
private @Nullable InvalidateCallback mInvalidateCallback;
|
private @Nullable InvalidateCallback mInvalidateCallback;
|
||||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||||
|
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
||||||
private int mDrawChildIndex = 0;
|
private int mDrawChildIndex = 0;
|
||||||
private boolean mIsAttached = false;
|
private boolean mIsAttached = false;
|
||||||
|
|
||||||
|
@ -59,6 +62,19 @@ import android.view.ViewParent;
|
||||||
super.detachAllViewsFromParent();
|
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
|
@Override
|
||||||
public void dispatchDraw(Canvas canvas) {
|
public void dispatchDraw(Canvas canvas) {
|
||||||
super.dispatchDraw(canvas);
|
super.dispatchDraw(canvas);
|
||||||
|
@ -143,6 +159,10 @@ import android.view.ViewParent;
|
||||||
mAttachDetachListeners = listeners;
|
mAttachDetachListeners = listeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ void mountNodeRegions(NodeRegion[] nodeRegions) {
|
||||||
|
mNodeRegions = nodeRegions;
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) {
|
/* package */ void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) {
|
||||||
for (int viewToAdd : viewsToAdd) {
|
for (int viewToAdd : viewsToAdd) {
|
||||||
if (viewToAdd > 0) {
|
if (viewToAdd > 0) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,8 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
new ElementsList<>(DrawCommand.EMPTY_ARRAY);
|
new ElementsList<>(DrawCommand.EMPTY_ARRAY);
|
||||||
private final ElementsList<AttachDetachListener> mAttachDetachListeners =
|
private final ElementsList<AttachDetachListener> mAttachDetachListeners =
|
||||||
new ElementsList<>(AttachDetachListener.EMPTY_ARRAY);
|
new ElementsList<>(AttachDetachListener.EMPTY_ARRAY);
|
||||||
|
private final ElementsList<NodeRegion> mNodeRegions =
|
||||||
|
new ElementsList<>(NodeRegion.EMPTY_ARRAY);
|
||||||
private final ElementsList<FlatShadowNode> mNativeChildren =
|
private final ElementsList<FlatShadowNode> mNativeChildren =
|
||||||
new ElementsList<>(FlatShadowNode.EMPTY_ARRAY);
|
new ElementsList<>(FlatShadowNode.EMPTY_ARRAY);
|
||||||
|
|
||||||
|
@ -56,7 +58,10 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
|
|
||||||
float left = node.getLayoutX();
|
float left = node.getLayoutX();
|
||||||
float top = node.getLayoutY();
|
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) {
|
if (mDetachAllChildrenFromViews != null) {
|
||||||
int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom);
|
int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom);
|
||||||
|
@ -90,6 +95,10 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
node.signalBackingViewIsCreated();
|
node.signalBackingViewIsCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addNodeRegion(NodeRegion nodeRegion) {
|
||||||
|
mNodeRegions.add(nodeRegion);
|
||||||
|
}
|
||||||
|
|
||||||
private void addNativeChild(FlatShadowNode nativeChild) {
|
private void addNativeChild(FlatShadowNode nativeChild) {
|
||||||
mNativeChildren.add(nativeChild);
|
mNativeChildren.add(nativeChild);
|
||||||
}
|
}
|
||||||
|
@ -130,6 +139,7 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
float height) {
|
float height) {
|
||||||
mDrawCommands.start(node.getDrawCommands());
|
mDrawCommands.start(node.getDrawCommands());
|
||||||
mAttachDetachListeners.start(node.getAttachDetachListeners());
|
mAttachDetachListeners.start(node.getAttachDetachListeners());
|
||||||
|
mNodeRegions.start(node.getNodeRegions());
|
||||||
mNativeChildren.start(node.getNativeChildren());
|
mNativeChildren.start(node.getNativeChildren());
|
||||||
|
|
||||||
collectStateRecursively(node, 0, 0, width, height);
|
collectStateRecursively(node, 0, 0, width, height);
|
||||||
|
@ -147,11 +157,18 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
node.setAttachDetachListeners(listeners);
|
node.setAttachDetachListeners(listeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final NodeRegion[] nodeRegions = mNodeRegions.finish();
|
||||||
|
if (nodeRegions != null) {
|
||||||
|
shouldUpdateMountState = true;
|
||||||
|
node.setNodeRegions(nodeRegions);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldUpdateMountState) {
|
if (shouldUpdateMountState) {
|
||||||
mOperationsQueue.enqueueUpdateMountState(
|
mOperationsQueue.enqueueUpdateMountState(
|
||||||
tag,
|
tag,
|
||||||
drawCommands,
|
drawCommands,
|
||||||
listeners);
|
listeners,
|
||||||
|
nodeRegions);
|
||||||
}
|
}
|
||||||
|
|
||||||
final FlatShadowNode[] nativeChildren = mNativeChildren.finish();
|
final FlatShadowNode[] nativeChildren = mNativeChildren.finish();
|
||||||
|
@ -254,6 +271,8 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
float right = left + width;
|
float right = left + width;
|
||||||
float bottom = top + height;
|
float bottom = top + height;
|
||||||
|
|
||||||
|
updateNodeRegion(node, tag, left, top, right, bottom);
|
||||||
|
|
||||||
if (node.mountsToView()) {
|
if (node.mountsToView()) {
|
||||||
ensureBackingViewIsCreated(node, tag, null);
|
ensureBackingViewIsCreated(node, tag, null);
|
||||||
|
|
||||||
|
@ -264,6 +283,21 @@ import com.facebook.react.uimanager.CatalystStylesDiffMap;
|
||||||
updateViewBounds(node, tag, left, top, right, bottom);
|
updateViewBounds(node, tag, left, top, right, bottom);
|
||||||
} else {
|
} else {
|
||||||
collectStateRecursively(node, left, top, right, bottom);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue