Files
react-native/ReactAndroid/src/main/java/com/facebook/react/flat/RCTView.java
T
Ahmed El-Helw e380d6d0fd Ignore touch events on layout only Nodes
Summary:
Before this patch, each Node would always generate a node
region, representing the bounds of this particular Node. This set of Nodes was
used when handling touch to figure out whether we should intercept touch (i.e.
a flat Node is catching the draw), or let Android handle touch (i.e. a Node
mounted to a View will handle the touch).

This patch modifies the list of NodeRegions to exclude any Nodes that draw
nothing at all. These Nodes, having no draw output, are effectively layout
containers used to group items, so they shouldn't handle touch.

Reviewed By: sriramramani

Differential Revision: D4369484

fbshipit-source-id: 71b41611873580631f1639f368fa8d971995874f
2016-12-28 18:43:39 -08:00

188 lines
5.3 KiB
Java

/**
* 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 javax.annotation.Nullable;
import android.graphics.Rect;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
/**
* Node for a react View.
*/
/* package */ final class RCTView extends FlatShadowNode {
private static final int[] SPACING_TYPES = {
Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM,
};
private @Nullable DrawBorder mDrawBorder;
boolean mRemoveClippedSubviews;
boolean mHorizontal;
private @Nullable Rect mHitSlop;
@Override
/* package */ void handleUpdateProperties(ReactStylesDiffMap styles) {
mRemoveClippedSubviews = mRemoveClippedSubviews ||
(styles.hasKey(PROP_REMOVE_CLIPPED_SUBVIEWS) &&
styles.getBoolean(PROP_REMOVE_CLIPPED_SUBVIEWS, false));
if (mRemoveClippedSubviews) {
mHorizontal = mHorizontal ||
(styles.hasKey(PROP_HORIZONTAL) && styles.getBoolean(PROP_HORIZONTAL, false));
}
super.handleUpdateProperties(styles);
}
@Override
protected void collectState(
StateBuilder stateBuilder,
float left,
float top,
float right,
float bottom,
float clipLeft,
float clipTop,
float clipRight,
float clipBottom) {
super.collectState(
stateBuilder,
left,
top,
right,
bottom,
clipLeft,
clipTop,
clipRight,
clipBottom);
if (mDrawBorder != null) {
mDrawBorder = (DrawBorder) mDrawBorder.updateBoundsAndFreeze(
left,
top,
right,
bottom,
clipLeft,
clipTop,
clipRight,
clipBottom);
stateBuilder.addDrawCommand(mDrawBorder);
}
}
@Override
boolean doesDraw() {
return mDrawBorder != null || super.doesDraw();
}
@Override
public void setBackgroundColor(int backgroundColor) {
getMutableBorder().setBackgroundColor(backgroundColor);
}
@Override
public void setBorderWidths(int index, float borderWidth) {
super.setBorderWidths(index, borderWidth);
int type = SPACING_TYPES[index];
getMutableBorder().setBorderWidth(type, PixelUtil.toPixelFromDIP(borderWidth));
}
@ReactProp(name = "nativeBackgroundAndroid")
public void setHotspot(@Nullable ReadableMap bg) {
if (bg != null) {
forceMountToView();
}
}
@ReactPropGroup(names = {
"borderColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor"
}, customType = "Color", defaultDouble = Double.NaN)
public void setBorderColor(int index, double color) {
int type = SPACING_TYPES[index];
if (Double.isNaN(color)) {
getMutableBorder().resetBorderColor(type);
} else {
getMutableBorder().setBorderColor(type, (int) color);
}
}
@ReactProp(name = "borderRadius")
public void setBorderRadius(float borderRadius) {
mClipRadius = borderRadius;
if (mClipToBounds && borderRadius > DrawView.MINIMUM_ROUNDED_CLIPPING_VALUE) {
// mount to a view if we are overflow: hidden and are clipping, so that we can do one
// clipPath to clip all the children of this node (both DrawCommands and Views).
forceMountToView();
}
getMutableBorder().setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius));
}
@ReactProp(name = "borderStyle")
public void setBorderStyle(@Nullable String borderStyle) {
getMutableBorder().setBorderStyle(borderStyle);
}
@ReactProp(name = "hitSlop")
public void setHitSlop(@Nullable ReadableMap hitSlop) {
if (hitSlop == null) {
mHitSlop = null;
} else {
mHitSlop = new Rect(
(int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("left")),
(int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("top")),
(int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("right")),
(int) PixelUtil.toPixelFromDIP(hitSlop.getDouble("bottom")));
}
}
@ReactProp(name = "pointerEvents")
public void setPointerEvents(@Nullable String pointerEventsStr) {
forceMountToView();
}
@Override
/* package */ void updateNodeRegion(
float left,
float top,
float right,
float bottom,
boolean isVirtual) {
if (!getNodeRegion().matches(left, top, right, bottom, isVirtual)) {
setNodeRegion(mHitSlop == null ?
new NodeRegion(left, top, right, bottom, getReactTag(), isVirtual) :
new HitSlopNodeRegion(mHitSlop, left, top, right, bottom, getReactTag(), isVirtual));
}
}
private DrawBorder getMutableBorder() {
if (mDrawBorder == null) {
mDrawBorder = new DrawBorder();
} else if (mDrawBorder.isFrozen()) {
mDrawBorder = (DrawBorder) mDrawBorder.mutableCopy();
}
invalidate();
return mDrawBorder;
}
@Override
public boolean clipsSubviews() {
return mRemoveClippedSubviews;
}
}