diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js index 180bccd6f7..8500e46d4d 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.android.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.android.js @@ -10,6 +10,7 @@ */ 'use strict'; +var Platform = require('Platform'); var PropTypes = require('react/lib/ReactPropTypes'); var React = require('React'); var ReactNative = require('react/lib/ReactNative'); @@ -41,6 +42,7 @@ var backgroundPropType = PropTypes.oneOfType([ var TouchableView = requireNativeComponent('RCTView', null, { nativeOnly: { nativeBackgroundAndroid: backgroundPropType, + nativeForegroundAndroid: backgroundPropType, } }); @@ -86,6 +88,17 @@ var TouchableNativeFeedback = React.createClass({ * methods to generate that dictionary. */ background: backgroundPropType, + + /** + * Set to true to add the ripple effect to the foreground of the view, instead of the + * background. This is useful if one of your child views has a background of its own, or you're + * e.g. displaying images, and you don't want the ripple to be covered by them. + * + * Check TouchableNativeFeedback.canUseNativeForeground() first, as this is only available on + * Android 6.0 and above. If you try to use this on older versions you will get a warning and + * fallback to background. + */ + useForeground: PropTypes.bool, }, statics: { @@ -117,6 +130,10 @@ var TouchableNativeFeedback = React.createClass({ Ripple: function(color: string, borderless: boolean) { return {type: 'RippleAndroid', color: processColor(color), borderless: borderless}; }, + + canUseNativeForeground: function() { + return Platform.OS === 'android' && Platform.Version >= 23; + } }, mixins: [Touchable.Mixin], @@ -213,9 +230,19 @@ var TouchableNativeFeedback = React.createClass({ } children.push(Touchable.renderDebugView({color: 'brown', hitSlop: this.props.hitSlop})); } + if (this.props.useForeground && !TouchableNativeFeedback.canUseNativeForeground()) { + console.warn( + 'Requested foreground ripple, but it is not available on this version of Android. ' + + 'Consider calling TouchableNativeFeedback.canUseNativeForeground() and using a different ' + + 'Touchable if the result is false.'); + } + const drawableProp = + this.props.useForeground && TouchableNativeFeedback.canUseNativeForeground() + ? 'nativeForegroundAndroid' + : 'nativeBackgroundAndroid'; var childProps = { ...child.props, - nativeBackgroundAndroid: this.props.background, + [drawableProp]: this.props.background, accessible: this.props.accessible !== false, accessibilityLabel: this.props.accessibilityLabel, accessibilityComponentType: this.props.accessibilityComponentType, diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index dce60c3fa8..44debcdc67 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -509,6 +509,7 @@ const View = React.createClass({ const RCTView = requireNativeComponent('RCTView', View, { nativeOnly: { nativeBackgroundAndroid: true, + nativeForegroundAndroid: true, } }); 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 18cf780612..1903f9168b 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 @@ -14,6 +14,7 @@ import javax.annotation.Nullable; import java.util.Locale; import java.util.Map; +import android.annotation.TargetApi; import android.graphics.Rect; import android.os.Build; import android.view.View; @@ -101,6 +102,14 @@ public class ReactViewManager extends ViewGroupManager { null : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg)); } + @TargetApi(Build.VERSION_CODES.M) + @ReactProp(name = "nativeForegroundAndroid") + public void setNativeForeground(ReactViewGroup view, @Nullable ReadableMap fg) { + view.setForeground(fg == null + ? null + : ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), fg)); + } + @ReactProp(name = com.facebook.react.uimanager.ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS) public void setRemoveClippedSubviews(ReactViewGroup view, boolean removeClippedSubviews) { view.setRemoveClippedSubviews(removeClippedSubviews);