From 4890d56eb1a96d04f6c8c54673ed75688f9e127a Mon Sep 17 00:00:00 2001 From: Ben Schwab Date: Mon, 30 Jan 2017 10:14:52 -0800 Subject: [PATCH] Adds corner-radius support to ripples created in TouchableNativeFeedback. (#10) This follows the same approach as b59f99b7e7f69b138860d12959eec9b46cd790a3 but fixes a bug in ReactViewGroup#refreshTranslucentBackgroundDrawable where a view that has a borderRadius, but no nativeBackground would not be rendered due to a missing condition case. --- Examples/UIExplorer/js/TouchableExample.js | 1 + .../react/views/view/ReactDrawableHelper.java | 24 ++++++-- .../view/ReactViewBackgroundDrawable.java | 56 ++++++++++--------- .../react/views/view/ReactViewGroup.java | 27 ++++++++- .../react/views/view/ReactViewManager.java | 3 +- 5 files changed, 75 insertions(+), 36 deletions(-) diff --git a/Examples/UIExplorer/js/TouchableExample.js b/Examples/UIExplorer/js/TouchableExample.js index 28a380d5f2c9ec..20513d856929ec 100644 --- a/Examples/UIExplorer/js/TouchableExample.js +++ b/Examples/UIExplorer/js/TouchableExample.js @@ -87,6 +87,7 @@ exports.examples = [ backgroundColor: 'rgb(180, 64, 119)', width: 200, height: 100, + borderRadius: 20, transform: [{scale: mScale}] }; return ( 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 a4dda0d9d3d89a..722f4e01a07a0a 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 @@ -12,8 +12,8 @@ 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.PaintDrawable; import android.graphics.drawable.RippleDrawable; import android.os.Build; import android.util.TypedValue; @@ -23,6 +23,8 @@ import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.uimanager.ViewProps; +import javax.annotation.Nullable; + /** * Utility class that helps with converting android drawable description used in JS to an actual * instance of {@link Drawable}. @@ -31,9 +33,16 @@ public class ReactDrawableHelper { private static final TypedValue sResolveOutValue = new TypedValue(); + public static Drawable createDrawableFromJSDescription( + Context context, + ReadableMap drawableDescriptionDict) { + return createDrawableFromJSDescription(context, drawableDescriptionDict, null); + } + public static Drawable createDrawableFromJSDescription( Context context, - ReadableMap drawableDescriptionDict) { + ReadableMap drawableDescriptionDict, + @Nullable float[] cornerRadii) { String type = drawableDescriptionDict.getString("type"); if ("ThemeAttrAndroid".equals(type)) { String attr = drawableDescriptionDict.getString("attribute"); @@ -75,11 +84,14 @@ public static Drawable createDrawableFromJSDescription( "couldn't be resolved into a drawable"); } } - Drawable mask = null; + PaintDrawable mask = null; if (!drawableDescriptionDict.hasKey("borderless") || - drawableDescriptionDict.isNull("borderless") || - !drawableDescriptionDict.getBoolean("borderless")) { - mask = new ColorDrawable(Color.WHITE); + drawableDescriptionDict.isNull("borderless") || + !drawableDescriptionDict.getBoolean("borderless")) { + mask = new PaintDrawable(Color.WHITE); + if (cornerRadii != null) { + mask.setCornerRadii(cornerRadii); + } } ColorStateList colorStateList = new ColorStateList( new int[][] {new int[]{}}, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index ab84ef512b7807..cd1f59615a179b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -255,6 +255,25 @@ private void drawRoundedBackgroundWithBorders(Canvas canvas) { } } + /* package */ float[] getBorderRadii() { + float defaultBorderRadius = !YogaConstants.isUndefined(mBorderRadius) ? mBorderRadius : 0; + float topLeftRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[0]) ? mBorderCornerRadii[0] : defaultBorderRadius; + float topRightRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[1]) ? mBorderCornerRadii[1] : defaultBorderRadius; + float bottomRightRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius; + float bottomLeftRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius; + + return new float[] { + topLeftRadius, + topLeftRadius, + topRightRadius, + topRightRadius, + bottomRightRadius, + bottomRightRadius, + bottomLeftRadius, + bottomLeftRadius + }; + } + private void updatePath() { if (!mNeedUpdatePathForBorderRadius) { return; @@ -277,25 +296,12 @@ private void updatePath() { mTempRectForBorderRadius.inset(fullBorderWidth * 0.5f, fullBorderWidth * 0.5f); } - float defaultBorderRadius = !YogaConstants.isUndefined(mBorderRadius) ? mBorderRadius : 0; - float topLeftRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[0]) ? mBorderCornerRadii[0] : defaultBorderRadius; - float topRightRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[1]) ? mBorderCornerRadii[1] : defaultBorderRadius; - float bottomRightRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius; - float bottomLeftRadius = mBorderCornerRadii != null && !YogaConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius; + float[] borderRadii = getBorderRadii(); mPathForBorderRadius.addRoundRect( - mTempRectForBorderRadius, - new float[] { - topLeftRadius, - topLeftRadius, - topRightRadius, - topRightRadius, - bottomRightRadius, - bottomRightRadius, - bottomLeftRadius, - bottomLeftRadius - }, - Path.Direction.CW); + mTempRectForBorderRadius, + borderRadii, + Path.Direction.CW); float extraRadiusForOutline = 0; @@ -306,14 +312,14 @@ private void updatePath() { mPathForBorderRadiusOutline.addRoundRect( mTempRectForBorderRadiusOutline, new float[] { - topLeftRadius + extraRadiusForOutline, - topLeftRadius + extraRadiusForOutline, - topRightRadius + extraRadiusForOutline, - topRightRadius + extraRadiusForOutline, - bottomRightRadius + extraRadiusForOutline, - bottomRightRadius + extraRadiusForOutline, - bottomLeftRadius + extraRadiusForOutline, - bottomLeftRadius + extraRadiusForOutline + borderRadii[0] + extraRadiusForOutline, + borderRadii[1] + extraRadiusForOutline, + borderRadii[2] + extraRadiusForOutline, + borderRadii[3] + extraRadiusForOutline, + borderRadii[4] + extraRadiusForOutline, + borderRadii[5] + extraRadiusForOutline, + borderRadii[6] + extraRadiusForOutline, + borderRadii[7] + extraRadiusForOutline }, Path.Direction.CW); } 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 f69abaf0252fda..acdfdb0b286a6f 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 @@ -9,8 +9,6 @@ package com.facebook.react.views.view; -import javax.annotation.Nullable; - import android.content.Context; import android.graphics.Color; import android.graphics.Rect; @@ -22,6 +20,7 @@ import android.view.ViewGroup; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.touch.ReactHitSlopView; import com.facebook.react.touch.ReactInterceptingViewGroup; @@ -32,6 +31,8 @@ import com.facebook.react.uimanager.ReactClippingViewGroupHelper; import com.facebook.react.uimanager.ReactPointerEventsView; +import javax.annotation.Nullable; + /** * Backing for a React View. Has support for borders, but since borders aren't common, lazy * initializes most of the storage needed for them. @@ -96,6 +97,7 @@ public void onLayoutChange( private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable; private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener; private boolean mNeedsOffscreenAlphaCompositing = false; + private @Nullable ReadableMap mNativeBackground; public ReactViewGroup(Context context) { super(context); @@ -136,16 +138,33 @@ public void setBackground(Drawable drawable) { "This method is not supported for ReactViewGroup instances"); } - public void setTranslucentBackgroundDrawable(@Nullable Drawable background) { + public void setNativeBackground(@Nullable ReadableMap nativeBackground) { + mNativeBackground = nativeBackground; + refreshTranslucentBackgroundDrawable(); + } + + public void refreshTranslucentBackgroundDrawable() { // it's required to call setBackground to null, as in some of the cases we may set new // background to be a layer drawable that contains a drawable that has been previously setup // as a background previously. This will not work correctly as the drawable callback logic is // messed up in AOSP + + Drawable background = null; + if (mNativeBackground != null) { + float[] cornerRadii = null; + if (mReactBackgroundDrawable != null) { + cornerRadii = mReactBackgroundDrawable.getBorderRadii(); + } + background = ReactDrawableHelper.createDrawableFromJSDescription(getContext(), mNativeBackground, cornerRadii); + } + super.setBackground(null); if (mReactBackgroundDrawable != null && background != null) { LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] {mReactBackgroundDrawable, background}); super.setBackground(layerDrawable); + } else if (mReactBackgroundDrawable != null && background == null) { + super.setBackground(mReactBackgroundDrawable); } else if (background != null) { super.setBackground(background); } @@ -209,10 +228,12 @@ public void setBorderColor(int position, float rgb, float alpha) { public void setBorderRadius(float borderRadius) { getOrCreateReactViewBackground().setRadius(borderRadius); + refreshTranslucentBackgroundDrawable(); } public void setBorderRadius(float borderRadius, int position) { getOrCreateReactViewBackground().setRadius(borderRadius, position); + refreshTranslucentBackgroundDrawable(); } public void setBorderStyle(@Nullable String style) { 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 b57fb619dcc0f0..1f67f8608d070b 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 @@ -104,8 +104,7 @@ public void setPointerEvents(ReactViewGroup view, @Nullable String pointerEvents @ReactProp(name = "nativeBackgroundAndroid") public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) { - view.setTranslucentBackgroundDrawable(bg == null ? - null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg)); + view.setNativeBackground(bg); } @TargetApi(Build.VERSION_CODES.M)