Skip to content

Commit

Permalink
Add pointerEvents prop to RN Android scroll views
Browse files Browse the repository at this point in the history
Summary:
Per discussion in the previous diff D33672110, it's ok to add the `pointerEvents` prop to scrollview. This will help prevent scrolling on the ScrollView if pointerEvents is set to `box-none`, or `none`.

Corresponding doc changes are in facebook/react-native-website#2936

Changelog:
[Android][Added] - Add new API in ScrollView and HorizontalScrollView to process pointerEvents prop.

Reviewed By: javache

Differential Revision: D33699223

fbshipit-source-id: 1cae5113e9e7d988fc4c4765c41d817a321804c4
  • Loading branch information
ryancat authored and facebook-github-bot committed Jan 21, 2022
1 parent d0f0234 commit 48f6967
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package com.facebook.react.uimanager;

import java.util.Locale;

/**
* Possible values for pointer events that a view and its descendants should receive. See
* https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events for more info.
Expand All @@ -25,4 +27,16 @@ public enum PointerEvents {
/** Container and all of its children receive touch events (like pointerEvents is unspecified). */
AUTO,
;

public static PointerEvents parsePointerEvents(String pointerEventsStr) {
return PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_"));
}

public static boolean canBeTouchTarget(PointerEvents pointerEvents) {
return pointerEvents == AUTO || pointerEvents == PointerEvents.BOX_ONLY;
}

public static boolean canChildrenBeTouchTarget(PointerEvents pointerEvents) {
return pointerEvents == PointerEvents.AUTO || pointerEvents == PointerEvents.BOX_NONE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.FabricViewStateManager;
import com.facebook.react.uimanager.MeasureSpecAssertions;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactClippingViewGroup;
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
import com.facebook.react.uimanager.ReactOverflowViewWithInset;
Expand Down Expand Up @@ -105,6 +106,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
private final ReactScrollViewScrollState mReactScrollViewScrollState;
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollX", 0, 0);
private PointerEvents mPointerEvents = PointerEvents.AUTO;

private final Rect mTempRect = new Rect();

Expand Down Expand Up @@ -452,6 +454,11 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}

// We intercept the touch event if the children are not supposed to receive it.
if (!PointerEvents.canChildrenBeTouchTarget(mPointerEvents)) {
return true;
}

try {
if (super.onInterceptTouchEvent(ev)) {
NativeGestureUtil.notifyNativeGestureStarted(this, ev);
Expand Down Expand Up @@ -519,6 +526,11 @@ public boolean onTouchEvent(MotionEvent ev) {
return false;
}

// We do not accept the touch event if this view is not supposed to receive it.
if (!PointerEvents.canBeTouchTarget(mPointerEvents)) {
return false;
}

mVelocityHelper.calculateVelocity(ev);
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_UP && mDragging) {
Expand Down Expand Up @@ -1253,4 +1265,12 @@ public int getFlingExtrapolatedDistance(int velocityX) {
this, velocityX, 0, Math.max(0, computeHorizontalScrollRange() - getWidth()), 0)
.x;
}

public void setPointerEvents(PointerEvents pointerEvents) {
mPointerEvents = pointerEvents;
}

public PointerEvents getPointerEvents() {
return mPointerEvents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.Spacing;
Expand Down Expand Up @@ -321,4 +322,13 @@ public void setContentOffset(ReactHorizontalScrollView view, ReadableMap value)
view.scrollTo(0, 0);
}
}

@ReactProp(name = ViewProps.POINTER_EVENTS)
public void setPointerEvents(ReactHorizontalScrollView view, @Nullable String pointerEventsStr) {
if (pointerEventsStr == null) {
view.setPointerEvents(PointerEvents.AUTO);
} else {
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.FabricViewStateManager;
import com.facebook.react.uimanager.MeasureSpecAssertions;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactClippingViewGroup;
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
import com.facebook.react.uimanager.ReactOverflowViewWithInset;
Expand Down Expand Up @@ -102,6 +103,7 @@ public class ReactScrollView extends ScrollView
private final ReactScrollViewScrollState mReactScrollViewScrollState =
new ReactScrollViewScrollState(ViewCompat.LAYOUT_DIRECTION_LTR);
private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollY", 0, 0);
private PointerEvents mPointerEvents = PointerEvents.AUTO;

public ReactScrollView(Context context) {
this(context, null);
Expand Down Expand Up @@ -332,6 +334,11 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}

// We intercept the touch event if the children are not supposed to receive it.
if (!PointerEvents.canChildrenBeTouchTarget(mPointerEvents)) {
return true;
}

try {
if (super.onInterceptTouchEvent(ev)) {
NativeGestureUtil.notifyNativeGestureStarted(this, ev);
Expand All @@ -357,6 +364,11 @@ public boolean onTouchEvent(MotionEvent ev) {
return false;
}

// We do not accept the touch event if this view is not supposed to receive it.
if (!PointerEvents.canBeTouchTarget(mPointerEvents)) {
return false;
}

mVelocityHelper.calculateVelocity(ev);
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_UP && mDragging) {
Expand Down Expand Up @@ -1115,4 +1127,12 @@ public int getFlingExtrapolatedDistance(int velocityY) {
return ReactScrollViewHelper.predictFinalScrollPosition(this, 0, velocityY, 0, getMaxScrollY())
.y;
}

public void setPointerEvents(PointerEvents pointerEvents) {
mPointerEvents = pointerEvents;
}

public PointerEvents getPointerEvents() {
return mPointerEvents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.Spacing;
Expand Down Expand Up @@ -363,4 +364,13 @@ public static Map<String, Object> createExportedCustomDirectEventTypeConstants()
MapBuilder.of("registrationName", "onMomentumScrollEnd"))
.build();
}

@ReactProp(name = ViewProps.POINTER_EVENTS)
public void setPointerEvents(ReactScrollView view, @Nullable String pointerEventsStr) {
if (pointerEventsStr == null) {
view.setPointerEvents(PointerEvents.AUTO);
} else {
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
}
}
}

0 comments on commit 48f6967

Please sign in to comment.