mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
allow custom ripple radius on TouchableNativeFeedback (#28009)
Summary: motivation: there are cases where one'd like to control the radius of the ripple effect that's present on TouchableNativeFeedback - in my case, I want to make sure that both icons and text have the same ripple appearance, but that's currently not possible as far as I can tell. Currently (afaik) the only way to set (upper) ripple limits is by specifying width, height and border radius ( + `overflow: hidden`), and this works well for icons which can usually be bounded by a square, but not for text which can have rectangular shape. This PR adds `rippleRadius` parameter to `SelectableBackground()`, `SelectableBackgroundBorderless()` and `Ripple()` static functions present on `TouchableNativeFeedback`. It can make the ripple smaller but also larger. The result looks like this: added to RNTester:  difference from the other ripples:  I'm ofc open to changing the api if needed, but I'm not sure there's much space for manoeuvring. While I was at it, I did a slight refactor of the class into several smaller, more focused methods. It's possible that in some cases, this might help to work around this issue https://github.com/facebook/react-native/issues/6480. ## Changelog [Android] [Added] - allow setting custom ripple radius on TouchableNativeFeedback Pull Request resolved: https://github.com/facebook/react-native/pull/28009 Test Plan: I tested this locally using RNTester Reviewed By: TheSavior Differential Revision: D20004509 Pulled By: mdvacca fbshipit-source-id: 10de1754d54c17878f36a3859705c1188f15c2a2
This commit is contained in:
committed by
Facebook Github Bot
parent
de8fcfb4cb
commit
7f2a79f40b
@@ -16,9 +16,13 @@ import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.os.Build;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.SoftAssertions;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
|
||||
/**
|
||||
@@ -41,48 +45,76 @@ public class ReactDrawableHelper {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Attribute " + attr + " couldn't be found in the resource list");
|
||||
}
|
||||
if (context.getTheme().resolveAttribute(attrID, sResolveOutValue, true)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return context
|
||||
.getResources()
|
||||
.getDrawable(sResolveOutValue.resourceId, context.getTheme());
|
||||
} else {
|
||||
return context.getResources().getDrawable(sResolveOutValue.resourceId);
|
||||
}
|
||||
} else {
|
||||
if (!context.getTheme().resolveAttribute(attrID, sResolveOutValue, true)) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Attribute " + attr + " couldn't be resolved into a drawable");
|
||||
"Attribute " + attr + " couldn't be resolved into a drawable");
|
||||
}
|
||||
Drawable drawable = getDefaultThemeDrawable(context);
|
||||
return setRadius(drawableDescriptionDict, drawable);
|
||||
} else if ("RippleAndroid".equals(type)) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Ripple drawable is not available on " + "android API <21");
|
||||
}
|
||||
int color;
|
||||
if (drawableDescriptionDict.hasKey(ViewProps.COLOR)
|
||||
&& !drawableDescriptionDict.isNull(ViewProps.COLOR)) {
|
||||
color = drawableDescriptionDict.getInt(ViewProps.COLOR);
|
||||
} else {
|
||||
if (context
|
||||
.getTheme()
|
||||
.resolveAttribute(android.R.attr.colorControlHighlight, sResolveOutValue, true)) {
|
||||
color = context.getResources().getColor(sResolveOutValue.resourceId);
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Attribute colorControlHighlight " + "couldn't be resolved into a drawable");
|
||||
}
|
||||
}
|
||||
Drawable mask = null;
|
||||
if (!drawableDescriptionDict.hasKey("borderless")
|
||||
|| drawableDescriptionDict.isNull("borderless")
|
||||
|| !drawableDescriptionDict.getBoolean("borderless")) {
|
||||
mask = new ColorDrawable(Color.WHITE);
|
||||
}
|
||||
ColorStateList colorStateList =
|
||||
new ColorStateList(new int[][] {new int[] {}}, new int[] {color});
|
||||
return new RippleDrawable(colorStateList, null, mask);
|
||||
RippleDrawable rd = getRippleDrawable(context, drawableDescriptionDict);
|
||||
return setRadius(drawableDescriptionDict, rd);
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException("Invalid type for android drawable: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private static Drawable getDefaultThemeDrawable(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return context
|
||||
.getResources()
|
||||
.getDrawable(sResolveOutValue.resourceId, context.getTheme());
|
||||
} else {
|
||||
return context.getResources().getDrawable(sResolveOutValue.resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
private static RippleDrawable getRippleDrawable(Context context, ReadableMap drawableDescriptionDict) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Ripple drawable is not available on " + "android API <21");
|
||||
}
|
||||
int color = getColor(context, drawableDescriptionDict);
|
||||
Drawable mask = getMask(drawableDescriptionDict);
|
||||
ColorStateList colorStateList =
|
||||
new ColorStateList(new int[][] {new int[] {}}, new int[] {color});
|
||||
|
||||
return new RippleDrawable(colorStateList, null, mask);
|
||||
}
|
||||
|
||||
private static Drawable setRadius(ReadableMap drawableDescriptionDict, Drawable drawable) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& drawableDescriptionDict.hasKey("rippleRadius")
|
||||
&& drawable instanceof RippleDrawable) {
|
||||
RippleDrawable rippleDrawable = (RippleDrawable) drawable;
|
||||
double rippleRadius = drawableDescriptionDict.getDouble("rippleRadius");
|
||||
rippleDrawable.setRadius((int) PixelUtil.toPixelFromDIP(rippleRadius));
|
||||
}
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private static int getColor(Context context, ReadableMap drawableDescriptionDict) {
|
||||
if (drawableDescriptionDict.hasKey(ViewProps.COLOR)
|
||||
&& !drawableDescriptionDict.isNull(ViewProps.COLOR)) {
|
||||
return drawableDescriptionDict.getInt(ViewProps.COLOR);
|
||||
} else {
|
||||
if (context
|
||||
.getTheme()
|
||||
.resolveAttribute(android.R.attr.colorControlHighlight, sResolveOutValue, true)) {
|
||||
return context.getResources().getColor(sResolveOutValue.resourceId);
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Attribute colorControlHighlight " + "couldn't be resolved into a drawable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Drawable getMask(ReadableMap drawableDescriptionDict) {
|
||||
if (!drawableDescriptionDict.hasKey("borderless")
|
||||
|| drawableDescriptionDict.isNull("borderless")
|
||||
|| !drawableDescriptionDict.getBoolean("borderless")) {
|
||||
return new ColorDrawable(Color.WHITE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user