From 0cc259a3f863f9337ac8bf4b32d3cff08f3b4d26 Mon Sep 17 00:00:00 2001 From: Nate Date: Thu, 20 Jun 2019 18:59:51 -0700 Subject: [PATCH] Set rounded rectangle mask on ripples This commit fixes an issue where ripple touch feedback extends beyond the border radius of a view. It achieves this by adding a mask to the RippleDrawable background, collecting that information from two new methods on ReactViewGroup: 1. getBorderRadiusMask() returns a drawable rounded rectangle matching the view's border radius properties 2. getBorderRadius() produces a float[] with the border radius information required to build a RoundedRectShape in getBorderRadiusMask() Additionally, this commit updates setBorderRadius in ReactViewManager to re-apply the background whenever it is set, which is necessary to update the mask on the RippleDrawable background image as the border radius changes. Related issues: https://github.com/facebook/react-native/issues/6480 --- .../react/views/view/ReactDrawableHelper.java | 8 ++-- .../react/views/view/ReactViewGroup.java | 38 +++++++++++++++++++ .../react/views/view/ReactViewManager.java | 17 ++++++++- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java index 8f68c957208c57..b4d7939088fc4b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactDrawableHelper.java @@ -9,8 +9,6 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.os.Build; @@ -30,7 +28,9 @@ public class ReactDrawableHelper { @TargetApi(Build.VERSION_CODES.LOLLIPOP) public static Drawable createDrawableFromJSDescription( - Context context, ReadableMap drawableDescriptionDict) { + ReactViewGroup view, + ReadableMap drawableDescriptionDict) { + Context context = view.getContext(); String type = drawableDescriptionDict.getString("type"); if ("ThemeAttrAndroid".equals(type)) { String attr = drawableDescriptionDict.getString("attribute"); @@ -75,7 +75,7 @@ public static Drawable createDrawableFromJSDescription( if (!drawableDescriptionDict.hasKey("borderless") || drawableDescriptionDict.isNull("borderless") || !drawableDescriptionDict.getBoolean("borderless")) { - mask = new ColorDrawable(Color.WHITE); + mask = view.getBorderRadiusMask(); } ColorStateList colorStateList = new ColorStateList(new int[][] {new int[] {}}, new int[] {color}); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index aafe2a692c2963..78da71a0208dd8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -18,6 +18,8 @@ import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; import android.os.Build; import android.view.MotionEvent; import android.view.View; @@ -282,10 +284,46 @@ public void setBorderRadius(float borderRadius, int position) { } } + public float[] getBorderRadius() { + final float borderRadius = mReactBackgroundDrawable.getFullBorderRadius(); + float topLeftBorderRadius = + mReactBackgroundDrawable.getBorderRadiusOrDefaultTo( + borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT); + float topRightBorderRadius = + mReactBackgroundDrawable.getBorderRadiusOrDefaultTo( + borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT); + float bottomLeftBorderRadius = + mReactBackgroundDrawable.getBorderRadiusOrDefaultTo( + borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT); + float bottomRightBorderRadius = + mReactBackgroundDrawable.getBorderRadiusOrDefaultTo( + borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT); + + return new float[] { + topLeftBorderRadius, + topLeftBorderRadius, + topRightBorderRadius, + topRightBorderRadius, + bottomRightBorderRadius, + bottomRightBorderRadius, + bottomLeftBorderRadius, + bottomLeftBorderRadius + }; + } + public void setBorderStyle(@Nullable String style) { getOrCreateReactViewBackground().setBorderStyle(style); } + public Drawable getBorderRadiusMask() { + RoundRectShape r = new RoundRectShape(getBorderRadius(), null, null); + + ShapeDrawable shapeDrawable = new ShapeDrawable(r); + shapeDrawable.getPaint().setColor(Color.WHITE); + + return shapeDrawable; + } + @Override public void setRemoveClippedSubviews(boolean removeClippedSubviews) { if (removeClippedSubviews == mRemoveClippedSubviews) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 7acaf8c48af824..165d9c120c6f9d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -52,6 +52,9 @@ public class ReactViewManager extends ViewGroupManager { private static final int CMD_SET_PRESSED = 2; private static final String HOTSPOT_UPDATE_KEY = "hotspotUpdate"; + private ReadableMap mBg = null; + private ReadableMap mFg = null; + @ReactProp(name = "accessible") public void setAccessible(ReactViewGroup view, boolean accessible) { view.setFocusable(accessible); @@ -118,6 +121,14 @@ public void setBorderRadius(ReactViewGroup view, int index, float borderRadius) } else { view.setBorderRadius(borderRadius, index - 1); } + + if (mBg != null) { + setNativeBackground(view, mBg); + } + + if (mFg != null) { + setNativeForeground(view, mFg); + } } @ReactProp(name = "borderStyle") @@ -158,19 +169,21 @@ public void setPointerEvents(ReactViewGroup view, @Nullable String pointerEvents @ReactProp(name = "nativeBackgroundAndroid") public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) { + mBg = bg; view.setTranslucentBackgroundDrawable( bg == null ? null - : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg)); + : ReactDrawableHelper.createDrawableFromJSDescription(view, bg)); } @TargetApi(Build.VERSION_CODES.M) @ReactProp(name = "nativeForegroundAndroid") public void setNativeForeground(ReactViewGroup view, @Nullable ReadableMap fg) { + mFg = fg; view.setForeground( fg == null ? null - : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), fg)); + : ReactDrawableHelper.createDrawableFromJSDescription(view, fg)); } @ReactProp(