Marcelo Petrucelli
Marcelo Petrucelli

Reputation: 178

Change RippleEffect Color back to it's Styled original value

I have an Android project where I'm changing the color of the Ripple effect that happens in one of my views using the following approach

Although it's working, I do need to reset this color back to it's default value, based on my style. Bellow I'll show my Style file, and I'd love to have a way to set the RippleDrawable back to it's default color.

<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="editTextColor">@android:color/white</item>
    <item name="font">@font/roboto_regular</item>

    <item name="colorControlNormal">@android:color/white</item>
    <item name="android:scrollbarThumbVertical">@drawable/nice_scrollbar</item>

    <item name="android:textColor">@color/darkGrey</item>
    <item name="android:editTextColor">@android:color/black</item>

    <item name="android:textColorHighlight">@color/colorPrimary</item>
    <item name="android:colorBackground">@color/darkerWhite</item>
    <item name="android:windowBackground">@color/darkerWhite</item>

    <item name="windowNoTitle">true</item>
    <item name="android:windowNoTitle">true</item>
</style>

As it can be noticed, I'm using MaterialComponents.

Below you'll find the current method I'm using to change color and also force the ripple effect on the view at a given x/y:

private void forceRippleAnimation(View view, int x, int y) {
    Drawable background = view.getBackground();
    if (Build.VERSION.SDK_INT >= 21 && background instanceof RippleDrawable) {
        RippleDrawable rippleDrawable = (RippleDrawable) background;
        rippleDrawable.setHotspot(x, y);
        rippleDrawable.setColor(new ColorStateList(
            new int[][]{
                new int[]{}
            },
            new int[]{
                getContext().getResources().getColor(R.color.rippleColor)
            }
        ));
        rippleDrawable.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});

        Handler handler = new Handler();
        handler.postDelayed(new Runnable(){
            @Override public void run(){
                rippleDrawable.setState(view.getDrawableState());
            }
        }, 650);
    }
}

Upvotes: 1

Views: 256

Answers (1)

B&#246; macht Blau
B&#246; macht Blau

Reputation: 13019

Instead of trying to reverse the changes made to the original background RippleDrawable, you can keep it and apply the changes to a copy:

Let the Activity have two Drawable fields:

private RippleDrawable customRippleDrawable;
private RippleDrawable backgroundFromXml;

Create a copy of the given background and set it as the new background:

Drawable background = view.getBackground();
if (Build.VERSION.SDK_INT >= 21 && background instanceof RippleDrawable) {
    backgroundFromXml = (RippleDrawable) background;
    customRippleDrawable = (RippleDrawable) background.getConstantState().newDrawable().mutate();
    customRippleDrawable.setHotspot(x, y);
    customRippleDrawable.setColor(new ColorStateList(
            new int[][]{
                new int[]{}
            },
            new int[]{
                MainActivity.this.getResources().getColor(R.color.rippleColor)
            }
    ));
    customRippleDrawable.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});
    view.setBackground(customRippleDrawable);
}

Since backgroundFromXml is a field, you can access it later on to reset the background to its original value:

view.setBackground(backgroundFromXml);

What does mutate() do?

All Drawables generated from one drawable resource share a common state. This helps saving resources (e.g. memory) and so will improve the performance of an android application.

Normally, if you apply for example a ColorFilter to a Drawable generated from a certain drawable resource, you will notice the effect everywhere in your app where this specific drawable resource is used.

Calling mutate() on a Drawable tells the runtime that this Drawable should have its own state. If you apply any changes afterwards, the other Drawables wil remain unchanged.

See also the documentation on mutate()

Upvotes: 2

Related Questions