diff --git a/Example/touchables/index.js b/Example/touchables/index.js index d86de64716..ed5390097e 100644 --- a/Example/touchables/index.js +++ b/Example/touchables/index.js @@ -260,6 +260,13 @@ const TOUCHABLES = [ renderChild: renderSampleBox, text: 'TouchableNativeFeedback (Ripple, borderless: true)', }, + { + type: TouchableNativeFeedback, + background: A => A.Ripple('blue', true, 30), + color: 'green', + renderChild: renderSampleBox, + text: 'TouchableNativeFeedback (Ripple, borderless: true, radius: 30)', + }, { type: TouchableNativeFeedback, background: A => A.Ripple('darkslategrey', false), diff --git a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.java b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.java index fd625bd62b..cc5a2cb913 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.java +++ b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.java @@ -13,6 +13,8 @@ import android.view.MotionEvent; import android.view.ViewGroup; +import com.facebook.react.bridge.SoftAssertions; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.ViewProps; @@ -29,10 +31,13 @@ static class ButtonViewGroup extends ViewGroup { int mBackgroundColor = Color.TRANSPARENT; // Using object because of handling null representing no value set. Integer mRippleColor; + Integer mRippleRadius; boolean mUseForeground = false; boolean mUseBorderless = false; float mBorderRadius = 0; boolean mNeedBackgroundUpdate = false; + public static final String SELECTABLE_ITEM_BACKGROUND = "selectableItemBackground"; + public static final String SELECTABLE_ITEM_BACKGROUND_BORDERLESS = "selectableItemBackgroundBorderless"; public ButtonViewGroup(Context context) { @@ -55,14 +60,18 @@ public void setRippleColor(Integer color) { mNeedBackgroundUpdate = true; } + public void setRippleRadius(Integer radius) { + mRippleRadius = radius; + mNeedBackgroundUpdate = true; + } + public void setBorderRadius(float borderRadius) { - mBorderRadius = borderRadius * (float)getResources().getDisplayMetrics().density; + mBorderRadius = borderRadius * getResources().getDisplayMetrics().density; mNeedBackgroundUpdate = true; } private Drawable applyRippleEffectWhenNeeded(Drawable selectable) { if (mRippleColor != null - && selectable != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && selectable instanceof RippleDrawable) { int[][] states = new int[][]{ new int[]{ android.R.attr.state_enabled } }; @@ -70,6 +79,12 @@ private Drawable applyRippleEffectWhenNeeded(Drawable selectable) { ColorStateList colorStateList = new ColorStateList(states, colors); ((RippleDrawable) selectable).setColor(colorStateList); } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && mRippleRadius != null + && selectable instanceof RippleDrawable) { + RippleDrawable rippleDrawable = (RippleDrawable) selectable; + rippleDrawable.setRadius((int) PixelUtil.toPixelFromDIP(mRippleRadius)); + } return selectable; } @@ -81,10 +96,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // We call `onTouchEvent` to and wait until button changes state to `pressed`, if it's pressed // we return true so that the gesture handler can activate onTouchEvent(ev); - if (isPressed()) { - return true; - } - return false; + return isPressed(); } private void updateBackground() { @@ -144,9 +156,9 @@ public void setUseBorderlessDrawable(boolean useBorderless) { private Drawable createSelectableDrawable() { final int version = Build.VERSION.SDK_INT; - String identifier = mUseBorderless && version >= 21 ? "selectableItemBackgroundBorderless" - : "selectableItemBackground"; - int attrID = getResources().getIdentifier(identifier, "attr", "android"); + String identifier = mUseBorderless && version >= 21 ? SELECTABLE_ITEM_BACKGROUND_BORDERLESS + : SELECTABLE_ITEM_BACKGROUND; + int attrID = getAttrId(getContext(), identifier); getContext().getTheme().resolveAttribute(attrID, sResolveOutValue, true); if (version >= 21) { return getResources().getDrawable(sResolveOutValue.resourceId, getContext().getTheme()); @@ -155,6 +167,18 @@ private Drawable createSelectableDrawable() { } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static int getAttrId(Context context, String attr) { + SoftAssertions.assertNotNull(attr); + if (SELECTABLE_ITEM_BACKGROUND.equals(attr)) { + return android.R.attr.selectableItemBackground; + } else if (SELECTABLE_ITEM_BACKGROUND_BORDERLESS.equals(attr)) { + return android.R.attr.selectableItemBackgroundBorderless; + } else { + return context.getResources().getIdentifier(attr, "attr", "android"); + } + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // No-op @@ -225,6 +249,11 @@ public void setRippleColor(ButtonViewGroup view, Integer rippleColor) { view.setRippleColor(rippleColor); } + @ReactProp(name = "rippleRadius") + public void setRippleRadius(ButtonViewGroup view, Integer rippleRadius) { + view.setRippleRadius(rippleRadius); + } + @Override protected void onAfterUpdateTransaction(ButtonViewGroup view) { view.updateBackground(); diff --git a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java index 1b2dc4e959..9bf0c8fccb 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java +++ b/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.java @@ -126,11 +126,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { mOrchestrator.onTouchEvent(ev); mPassingTouch = false; - if (mShouldIntercept) { - return true; - } else { - return false; - } + return mShouldIntercept; } private void tryCancelAllHandlers() { diff --git a/touchables/TouchableNativeFeedback.android.js b/touchables/TouchableNativeFeedback.android.js index 493b165b9c..774111f7ff 100644 --- a/touchables/TouchableNativeFeedback.android.js +++ b/touchables/TouchableNativeFeedback.android.js @@ -10,14 +10,19 @@ import GenericTouchable from './GenericTouchable'; * it follows native behaviours. */ export default class TouchableNativeFeedback extends Component { - static SelectableBackground = () => ({ type: 'SelectableBackground' }); - static SelectableBackgroundBorderless = () => ({ + static SelectableBackground = rippleRadius => ({ + type: 'SelectableBackground', + rippleRadius, + }); + static SelectableBackgroundBorderless = rippleRadius => ({ type: 'SelectableBackgroundBorderless', + rippleRadius, }); - static Ripple = (color, borderless) => ({ + static Ripple = (color, borderless, rippleRadius) => ({ type: 'Ripple', color, borderless, + rippleRadius, }); static canUseNativeForeground = () => Platform.Version >= 23; @@ -38,20 +43,22 @@ export default class TouchableNativeFeedback extends Component { style: PropTypes.any, }; - getExtraButtonProps = () => { + getExtraButtonProps() { const extraProps = {}; const { background } = this.props; if (background) { if (background.type === 'Ripple') { extraProps['borderless'] = background.borderless; extraProps['rippleColor'] = background.color; + extraProps['rippleRadius'] = background.rippleRadius; } else if (background.type === 'SelectableBackgroundBorderless') { extraProps['borderless'] = true; + extraProps['rippleRadius'] = background.rippleRadius; } } extraProps['foreground'] = this.props.useForeground; return extraProps; - }; + } render() { const { style = {}, ...rest } = this.props; return (