diff --git a/Examples/UIExplorer/TouchableExample.js b/Examples/UIExplorer/TouchableExample.js
index 5e7243c8d..6c19c4086 100644
--- a/Examples/UIExplorer/TouchableExample.js
+++ b/Examples/UIExplorer/TouchableExample.js
@@ -110,6 +110,14 @@ exports.examples = [
render: function(): ReactElement {
return ;
},
+ }, {
+ title: 'TouchableNativeFeedback with Border Radius',
+ description: 'The ripples from should follow borderRadius. ' +
+ 'This only works with a non-borderless background from TouchableNativeFeedback.Ripple',
+ platform: 'android',
+ render: function(): ReactElement {
+ return ;
+ },
}];
var TextOnPressBox = React.createClass({
@@ -368,6 +376,25 @@ var TouchableDisabled = React.createClass({
}
});
+var TouchableNativeFeedbackBorderRadius = React.createClass({
+ render: function() {
+ return (
+
+ console.log('TouchableNativeFeedback Ripple with Border Radius has been clicked')}
+ background={TouchableNativeFeedback.Ripple('#ccc')}>
+
+
+ TouchableNativeFeedback.Ripple with Border Radius
+
+
+
+
+ );
+ }
+});
+
var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'};
var styles = StyleSheet.create({
@@ -441,4 +468,10 @@ var styles = StyleSheet.create({
fontWeight: '500',
color: 'blue',
},
+ tnfBorderRadiusView: {
+ borderTopLeftRadius: 10,
+ borderTopRightRadius: 5,
+ borderBottomLeftRadius: 0,
+ borderBottomRightRadius: 20
+ }
});
diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js
index 4fe522732..af158fdf2 100644
--- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js
+++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js
@@ -92,6 +92,9 @@ var TouchableNativeFeedback = React.createClass({
/**
* Creates an object that represents android theme's default background for
* selectable elements (?android:attr/selectableItemBackground).
+ *
+ * The backgrounds generated by this method DO NOT follow the child view's
+ * border radii styles. To clip the background with border radii, use Ripple.
*/
SelectableBackground: function() {
return {type: 'ThemeAttrAndroid', attribute: 'selectableItemBackground'};
@@ -111,6 +114,9 @@ var TouchableNativeFeedback = React.createClass({
* example of that behavior). This background type is available on Android
* API level 21+.
*
+ * The non-borderless backgrounds generated by this method follows the child
+ * view's border radii styles (e.g. the ripples are clipped by the border radii).
+ *
* @param color The ripple color
* @param borderless If the ripple can render outside it's bounds
*/
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 a4dda0d9d..4e6ce5e2a 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,10 +9,12 @@
package com.facebook.react.views.view;
+import javax.annotation.Nullable;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
@@ -34,6 +36,13 @@ public class ReactDrawableHelper {
public static Drawable createDrawableFromJSDescription(
Context context,
ReadableMap drawableDescriptionDict) {
+ return createDrawableFromJSDescription(context, drawableDescriptionDict, null);
+ }
+
+ public static Drawable createDrawableFromJSDescription(
+ Context context,
+ ReadableMap drawableDescriptionDict,
+ @Nullable float[] cornerRadii) {
String type = drawableDescriptionDict.getString("type");
if ("ThemeAttrAndroid".equals(type)) {
String attr = drawableDescriptionDict.getString("attribute");
@@ -75,11 +84,14 @@ public class ReactDrawableHelper {
"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);
+ 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 fd8961cb8..d2b65797f 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
@@ -236,6 +236,25 @@ import com.facebook.csslayout.Spacing;
}
}
+ /* package */ float[] getBorderRadii() {
+ float defaultBorderRadius = !CSSConstants.isUndefined(mBorderRadius) ? mBorderRadius : 0;
+ float topLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[0]) ? mBorderCornerRadii[0] : defaultBorderRadius;
+ float topRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[1]) ? mBorderCornerRadii[1] : defaultBorderRadius;
+ float bottomRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius;
+ float bottomLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius;
+
+ return new float[] {
+ topLeftRadius,
+ topLeftRadius,
+ topRightRadius,
+ topRightRadius,
+ bottomRightRadius,
+ bottomRightRadius,
+ bottomLeftRadius,
+ bottomLeftRadius
+ };
+ }
+
private void updatePath() {
if (!mNeedUpdatePathForBorderRadius) {
return;
@@ -258,25 +277,10 @@ import com.facebook.csslayout.Spacing;
mTempRectForBorderRadius.inset(fullBorderWidth * 0.5f, fullBorderWidth * 0.5f);
}
- float defaultBorderRadius = !CSSConstants.isUndefined(mBorderRadius) ? mBorderRadius : 0;
- float topLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[0]) ? mBorderCornerRadii[0] : defaultBorderRadius;
- float topRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[1]) ? mBorderCornerRadii[1] : defaultBorderRadius;
- float bottomRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius;
- float bottomLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius;
-
-
+ float[] borderRadii = getBorderRadii();
mPathForBorderRadius.addRoundRect(
mTempRectForBorderRadius,
- new float[] {
- topLeftRadius,
- topLeftRadius,
- topRightRadius,
- topRightRadius,
- bottomRightRadius,
- bottomRightRadius,
- bottomLeftRadius,
- bottomLeftRadius
- },
+ borderRadii,
Path.Direction.CW);
float extraRadiusForOutline = 0;
@@ -288,14 +292,14 @@ import com.facebook.csslayout.Spacing;
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 63ceac761..5ace0a417 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
@@ -22,6 +22,7 @@ import android.view.View;
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;
@@ -94,6 +95,7 @@ public class ReactViewGroup extends ViewGroup implements
private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable;
private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener;
private boolean mNeedsOffscreenAlphaCompositing = false;
+ private @Nullable ReadableMap mNativeBackground;
public ReactViewGroup(Context context) {
super(context);
@@ -134,7 +136,21 @@ public class ReactViewGroup extends ViewGroup implements
"This method is not supported for ReactViewGroup instances");
}
- public void setTranslucentBackgroundDrawable(@Nullable Drawable background) {
+ public void setNativeBackground(@Nullable ReadableMap nativeBackground) {
+ mNativeBackground = nativeBackground;
+ refreshTranslucentBackgroundDrawable();
+ }
+
+ private void refreshTranslucentBackgroundDrawable() {
+ Drawable background = null;
+ if (mNativeBackground != null) {
+ float[] cornerRadii = null;
+ if (mReactBackgroundDrawable != null) {
+ cornerRadii = mReactBackgroundDrawable.getBorderRadii();
+ }
+ background = ReactDrawableHelper.createDrawableFromJSDescription(getContext(), mNativeBackground, cornerRadii);
+ }
+
// 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
@@ -207,10 +223,12 @@ public class ReactViewGroup extends ViewGroup implements
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 6a4bd5d26..d20eee96f 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
@@ -101,8 +101,7 @@ public class ReactViewManager extends ViewGroupManager {
@ReactProp(name = "nativeBackgroundAndroid")
public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) {
- view.setTranslucentBackgroundDrawable(bg == null ?
- null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg));
+ view.setNativeBackground(bg);
}
@ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)