How to use RippleDrawable programmatically in code (not xml) with Android 5.0 Lollipop?
Asked Answered
L

4

35

I have the following code for my ripple:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:colorControlHighlight">
    <item android:id="@+id/rip">

        <shape android:shape="oval">
            <solid android:color="?android:colorAccent"/>
        </shape>
    </item>
</ripple>

Now I want to give the user the possibility to choose own colors, so I need to create the ripple programmatically.
I found this and I think this is the right way to do it, but I don't know how to handle with this.

The ripple will be used here:

<ImageButton
    android:id="@+id/add_button"
    android:layout_width="@dimen/diameter"
    android:layout_height="@dimen/diameter"
    android:layout_gravity="end|bottom"
    android:layout_marginBottom="@dimen/add_button_margin"
    android:layout_marginEnd="@dimen/add_button_margin"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:src="@drawable/ic_action_add_person"
    android:tint="@android:color/white"
    android:background="@drawable/oval_ripple"
    android:elevation="@dimen/elevation_low"
    android:stateListAnimator="@anim/button_elevation"
    android:contentDescription="Neuer Spieler" />

I need to set the background to a RippleDrawable like this:

addButton.setBackground(ripple);
Letti answered 5/1, 2015 at 21:4 Comment(6)
Where do you use your xml resource?Pellicle
@Pellicle I edited the first post :)Letti
so whats the problem with creating a new RippleDrawable?Pellicle
I don't know how to work with it.Letti
Drawable ripple = new RippleDrawable(...)Pellicle
Yeah, that us what I already know. But I don't k ow how to create one, which looks like my XMLrippleLetti
S
68

This is how I was able to achieve this.

Note that this is Api 21+ only so you will have to fallback to a normal Drawable if you support lower versions.

public static RippleDrawable getPressedColorRippleDrawable(int normalColor, int pressedColor)
{
    return new RippleDrawable(getPressedColorSelector(normalColor, pressedColor), getColorDrawableFromColor(normalColor), null);
}

public static ColorStateList getPressedColorSelector(int normalColor, int pressedColor)
{
    return new ColorStateList(
        new int[][]
            {
                new int[]{android.R.attr.state_pressed},
                new int[]{android.R.attr.state_focused},
                new int[]{android.R.attr.state_activated},
                new int[]{}
            },
        new int[]
            {
                pressedColor,
                pressedColor,
                pressedColor,
                normalColor
            }
    );
}

public static ColorDrawable getColorDrawableFromColor(int color)
{
    return new ColorDrawable(color);
}

Edit: I tinkered with this some more and discovered that the ColorStateList doesn't need to be nearly as complex as the above solution. I have simplified it to the below snippet. (Everything else in the above code block is the same. I only changed the ColorStateList creation.) I will leave the above block as the original, in case this method doesn't work for someone.

new ColorStateList(
    new int[][]
        {
            new int[]{}
        },
    new int[]
        {
            pressedColor
        }
);
Selection answered 5/2, 2015 at 20:59 Comment(5)
Thank you VERY much for you answer :) (Can't give you an upvote, not enough reputations).Letti
Sure thing; it was a pain to figure out so I'm happy to share. :) Did it work for you? I tinkered with it some more and learned that I could greatly simplify the ColorStateList and get the same result so I edited my answer to include that as well.Selection
You can also do it using ColorStateList.valueOf(rippleColor);Milled
Please note that this solution adds a mask. So if you have, let's say a blue button and set the pressedColor as yellow, then the ripple will be purple-ish.Acknowledge
@JoaquinIurchuk do you how to fix it ? rippleDrawable.setId(index, android.R.id.mask) didn't work.Colombi
A
25
public static Drawable getAdaptiveRippleDrawable(
    int normalColor, int pressedColor) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return new RippleDrawable(ColorStateList.valueOf(pressedColor),
                null, getRippleMask(normalColor));
    } else {
        return getStateListDrawable(normalColor, pressedColor);
    }
}

private static Drawable getRippleMask(int color) {
    float[] outerRadii = new float[8];
    // 3 is radius of final ripple, 
    // instead of 3 you can give required final radius
    Arrays.fill(outerRadii, 3);

    RoundRectShape r = new RoundRectShape(outerRadii, null, null);
    ShapeDrawable shapeDrawable = new ShapeDrawable(r);
    shapeDrawable.getPaint().setColor(color);
    return shapeDrawable;
}

public static StateListDrawable getStateListDrawable(
    int normalColor, int pressedColor) {
    StateListDrawable states = new StateListDrawable();
    states.addState(new int[]{android.R.attr.state_pressed}, 
        new ColorDrawable(pressedColor));
    states.addState(new int[]{android.R.attr.state_focused}, 
        new ColorDrawable(pressedColor));
    states.addState(new int[]{android.R.attr.state_activated}, 
        new ColorDrawable(pressedColor));
    states.addState(new int[]{}, 
        new ColorDrawable(normalColor));
    return states;
}

You can get the drawable and apply to any view using view.setDrawable.
For Lollipop+ devices you will get ripple else it will change the color of view.

Apoplectic answered 19/9, 2015 at 7:3 Comment(3)
This is nice. A drawback, I think, is that it might loose the rounded corners when applied on a pre-lollipop device, because the ColorDrawables in the StateListDrawable have no shape. We could provide colored ShapeDrawable instead to keep the shape. But maybe this would start to get a little heavy. In that case, yet another solution would be to create a custom "ColorStateDrawable" with a "setColor" method avoiding you to re-instantiate all the objects needed for a StateListDrawable.Martimartial
Found something close here : github.com/gabrielemariotti/colorpickercollection/blob/master/…Martimartial
"normalColor" does not work! it's always transparentIdolater
M
2

Basically, you need to create a new RippleDrawable object. For pre-Lollipop devices, you want a StateListDrawable (as others already suggested). I've written a somewhat resourceful GIST with a bunch of useful methods related to Drawables and coloring: https://gist.github.com/milosmns/6566ca9e3b756d922aa5

Most likely you will want to use #getBackgroundDrawable() from that singleton.

Milled answered 5/12, 2015 at 23:42 Comment(1)
It's super huge, I'm not sure it is relevant to post a chain of ten 50-line methods here. It's my gist so it's being kept alive intentionally :)Milled
B
0

You can assign it as foreground, in Kotlin its:

view.foreground = ContextCompat.getDrawable(context, R.drawable.rippleRes)
Breadstuff answered 13/8, 2021 at 12:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.