Make sparkle color and alpha customizable

This CL introduces a new attr: effectColor.
The attribute can be used on ripple tags to customize the color and
alpha of the ripple decoration.

It's also possible to change the effect color via code by invoking
RippleDrawable#setEffectColor.

Fixes: 185602477
Test: atest RippleDrawableTest
Change-Id: I9b50f330729a67ef4b529283a92c83c28387fa0b
This commit is contained in:
Lucas Dupin
2021-04-16 12:00:56 -07:00
parent 514f8b70d8
commit 9548384364
10 changed files with 61 additions and 60 deletions

View File

@@ -592,6 +592,7 @@ package android {
field public static final int editTextStyle = 16842862; // 0x101006e
field @Deprecated public static final int editable = 16843115; // 0x101016b
field public static final int editorExtras = 16843300; // 0x1010224
field public static final int effectColor;
field public static final int elegantTextHeight = 16843869; // 0x101045d
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
@@ -16817,8 +16818,10 @@ package android.graphics.drawable {
public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
ctor public RippleDrawable(@NonNull android.content.res.ColorStateList, @Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable);
method @NonNull public android.content.res.ColorStateList getEffectColor();
method public int getRadius();
method public void setColor(android.content.res.ColorStateList);
method public void setColor(@NonNull android.content.res.ColorStateList);
method public void setEffectColor(@NonNull android.content.res.ColorStateList);
method public void setRadius(int);
field public static final int RADIUS_AUTO = -1; // 0xffffffff
}

View File

@@ -15,8 +15,7 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight"
android:rippleStyle="?attr/rippleStyle">
android:color="?attr/colorControlHighlight">
<item android:id="@id/mask"
android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>

View File

@@ -19,8 +19,7 @@
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<ripple android:color="?attr/colorControlHighlight"
android:rippleStyle="?attr/rippleStyle">
<ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle"
android:tint="@color/btn_colored_background_material">

View File

@@ -15,7 +15,6 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight"
android:rippleStyle="?attr/rippleStyle">
android:color="?attr/colorControlHighlight">
<item android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>

View File

@@ -21,8 +21,7 @@
android:insetBottom="@dimen/button_inset_vertical_material">
<layer-list android:paddingMode="stack">
<item>
<ripple android:color="?attr/colorControlHighlight"
android:rippleStyle="?attr/rippleStyle">
<ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorButtonNormal">

View File

@@ -1176,6 +1176,9 @@
<!-- Alternative color applied to surfaces on top of colorBackground. @hide -->
<attr name="colorSurfaceHeader" format="color" />
<!-- Color applied to effects. -->
<attr name="effectColor" format="color" />
<!-- The type of the edge effect. The default is glow. -->
<attr name="edgeEffectType">
<!-- Use a colored glow at the edge. -->
@@ -6540,13 +6543,8 @@
<!-- The radius of the ripple when fully expanded. By default, the
radius is computed based on the size of the ripple's container. -->
<attr name="radius" />
<!-- The style of the ripple drawable is solid by default -->
<attr name="rippleStyle">
<!-- Solid is the default style -->
<enum name="solid" value="0" />
<!-- Patterned style-->
<enum name="patterned" value="1" />
</attr>
<!-- Secondary color of the ripple effect. -->
<attr name="effectColor" />
</declare-styleable>
<declare-styleable name="ScaleDrawable">

View File

@@ -3099,6 +3099,7 @@
<!-- @hide @SystemApi -->
<public name="throttleDurationMillis" />
<public name="showInInputMethodPicker" />
<public name="effectColor" />
</staging-public-group>
<staging-public-group type="drawable" first-id="0x010800b5">

View File

@@ -234,9 +234,6 @@ easier.
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<item name="colorForeground">@color/foreground_device_default_dark</item>
<item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Ripple style-->
<item name="rippleStyle">solid</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />

View File

@@ -150,6 +150,7 @@ public class RippleDrawable extends LayerDrawable {
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
private static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final int DEFAULT_EFFECT_COLOR = 0x80ffffff;
/** Temporary flag for teamfood. **/
private static final boolean FORCE_PATTERNED_STYLE = true;
@@ -465,11 +466,36 @@ public class RippleDrawable extends LayerDrawable {
*
* @attr ref android.R.styleable#RippleDrawable_color
*/
public void setColor(ColorStateList color) {
public void setColor(@NonNull ColorStateList color) {
if (color == null) {
throw new IllegalArgumentException("color cannot be null");
}
mState.mColor = color;
invalidateSelf(false);
}
/**
* Sets the ripple effect color.
*
* @param color Ripple color as a color state list.
*
* @attr ref android.R.styleable#RippleDrawable_effectColor
*/
public void setEffectColor(@NonNull ColorStateList color) {
if (color == null) {
throw new IllegalArgumentException("color cannot be null");
}
mState.mEffectColor = color;
invalidateSelf(false);
}
/**
* @return The ripple effect color as a color state list.
*/
public @NonNull ColorStateList getEffectColor() {
return mState.mEffectColor;
}
/**
* Sets the radius in pixels of the fully expanded ripple.
*
@@ -561,13 +587,14 @@ public class RippleDrawable extends LayerDrawable {
mState.mColor = color;
}
final ColorStateList effectColor =
a.getColorStateList(R.styleable.RippleDrawable_effectColor);
if (effectColor != null) {
mState.mEffectColor = effectColor;
}
mState.mMaxRadius = a.getDimensionPixelSize(
R.styleable.RippleDrawable_radius, mState.mMaxRadius);
if (!FORCE_PATTERNED_STYLE) {
mState.mRippleStyle = a.getInteger(R.styleable.RippleDrawable_rippleStyle,
mState.mRippleStyle);
}
}
private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
@@ -942,10 +969,11 @@ public class RippleDrawable extends LayerDrawable {
float radius = getComputedRadius();
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
RippleShader shader = new RippleShader();
int color = mMaskColorFilter == null
final int color = mMaskColorFilter == null
? mState.mColor.getColorForState(getState(), Color.BLACK)
: mMaskColorFilter.getColor();
shader.setColor(color);
final int effectColor = mState.mEffectColor.getColorForState(getState(), Color.MAGENTA);
shader.setColor(color, effectColor);
shader.setOrigin(cx, cy);
shader.setTouch(x, y);
shader.setResolution(w, h, mState.mDensity);
@@ -1266,33 +1294,6 @@ public class RippleDrawable extends LayerDrawable {
return this;
}
/**
* Sets the visual style of the ripple.
*
* @see #STYLE_SOLID
* @see #STYLE_PATTERNED
*
* @param style The style of the ripple
* @hide
*/
public void setRippleStyle(@RippleStyle int style) throws IllegalArgumentException {
if (style == STYLE_SOLID || style == STYLE_PATTERNED) {
mState.mRippleStyle = style;
} else {
throw new IllegalArgumentException("Invalid style value " + style);
}
}
/**
* Get the current ripple style
* @return Ripple style
* @hide
*/
public @RippleStyle int getRippleStyle() {
return mState.mRippleStyle;
}
@Override
RippleState createConstantState(LayerState state, Resources res) {
return new RippleState(state, this, res);
@@ -1302,6 +1303,7 @@ public class RippleDrawable extends LayerDrawable {
int[] mTouchThemeAttrs;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
ColorStateList mEffectColor = ColorStateList.valueOf(DEFAULT_EFFECT_COLOR);
int mMaxRadius = RADIUS_AUTO;
int mRippleStyle = FORCE_PATTERNED_STYLE ? STYLE_PATTERNED : STYLE_SOLID;
@@ -1314,6 +1316,7 @@ public class RippleDrawable extends LayerDrawable {
mColor = origs.mColor;
mMaxRadius = origs.mMaxRadius;
mRippleStyle = origs.mRippleStyle;
mEffectColor = origs.mEffectColor;
if (origs.mDensity != mDensity) {
applyDensityScaling(orig.mDensity, mDensity);

View File

@@ -39,6 +39,7 @@ final class RippleShader extends RuntimeShader {
+ "uniform vec2 in_tRotation2;\n"
+ "uniform vec2 in_tRotation3;\n"
+ "uniform vec4 in_color;\n"
+ "uniform vec4 in_sparkleColor;\n"
+ "uniform shader in_shader;\n";
private static final String SHADER_LIB =
"float triangleNoise(vec2 n) {\n"
@@ -48,7 +49,6 @@ final class RippleShader extends RuntimeShader {
+ " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ "}"
+ "const float PI = 3.1415926535897932384626;\n"
+ "const float SPARKLE_OPACITY = 0.75;\n"
+ "\n"
+ "float sparkles(vec2 uv, float t) {\n"
+ " float n = triangleNoise(uv);\n"
@@ -60,7 +60,7 @@ final class RippleShader extends RuntimeShader {
+ " o *= abs(sin(PI * o * (t + 0.55 * i)));\n"
+ " s += o;\n"
+ " }\n"
+ " return saturate(s) * SPARKLE_OPACITY;\n"
+ " return saturate(s) * in_sparkleColor.a;\n"
+ "}\n"
+ "float softCircle(vec2 uv, vec2 xy, float radius, float blur) {\n"
+ " float blurHalf = blur * 0.5;\n"
@@ -116,7 +116,7 @@ final class RippleShader extends RuntimeShader {
+ " vec4 circle = in_color * (softCircle(p, center, in_maxRadius "
+ " * scaleIn, 0.2) * fade);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
+ " return mix(circle, vec4(sparkle), sparkle) * mask;\n"
+ " return mix(circle, in_sparkleColor, sparkle) * mask;\n"
+ "}";
private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125;
@@ -197,10 +197,13 @@ final class RippleShader extends RuntimeShader {
/**
* Color of the circle that's under the sparkles. Sparkles will always be white.
*/
public void setColor(@ColorInt int colorIn) {
Color color = Color.valueOf(colorIn);
this.setUniform("in_color", new float[] {color.red(),
public void setColor(@ColorInt int colorInt, @ColorInt int sparkleColorInt) {
Color color = Color.valueOf(colorInt);
Color sparkleColor = Color.valueOf(sparkleColorInt);
setUniform("in_color", new float[] {color.red(),
color.green(), color.blue(), color.alpha()});
setUniform("in_sparkleColor", new float[] {sparkleColor.red(),
sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()});
}
public void setResolution(float w, float h, int density) {