如何在Android 5.0 Lollipop中使用RippleDrawable编程实现(而非XML)?

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>

现在我想让用户有可能选择自己的颜色,因此我需要通过程序创建涟漪效果。


我找到了这个,我认为这是正确的方法,但是我不知道如何处理它。


这里将使用涟漪效果:

<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" />

我需要将背景设置为像这样的RippleDrawable

addButton.setBackground(ripple);

你在哪里使用你的XML资源? - pskink
@pskink 我编辑了第一篇帖子 :) - ich5003
创建一个新的RippleDrawable有什么问题? - pskink
我不知道如何处理它。 - ich5003
可绘制的涟漪 = 新的RippleDrawable(...) - pskink
是的,我已经知道了。但我不知道如何创建一个看起来像我的XMLripple的东西。 - ich5003
4个回答

68

这是我如何实现的。

请注意,这仅适用于Api 21及以上版本,如果您支持较低版本,则必须回退到普通Drawable。

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);
}

编辑: 我对此进行了一些调整,并发现ColorStateList不需要像上面的解决方案那样复杂。我已将其简化为以下代码片段。(上述代码块中的所有其他内容都相同。我只更改了ColorStateList的创建方式。)如果这种方法不适用于某人,我将保留上面的代码块作为原始代码。

new ColorStateList(
    new int[][]
        {
            new int[]{}
        },
    new int[]
        {
            pressedColor
        }
);

6
非常感谢您的回答 :) - ich5003
1
当然可以;找出来真是费了好大的劲,所以我很高兴能分享给你。 :) 它对你有用吗?我又调试了一下,发现我可以大大简化ColorStateList并获得相同的结果,所以我编辑了我的答案,也包括了这个。 - kdenney
20
您可以使用ColorStateList.valueOf(rippleColor)来完成此操作。 - milosmns
2
请注意,此解决方案添加了一个遮罩。因此,如果您有一个蓝色按钮,并将pressedColor设置为黄色,则涟漪将呈紫色。 - Joaquin Iurchuk
@JoaquinIurchuk,你知道如何修复它吗?rippleDrawable.setId(index, android.R.id.mask)没有起作用。 - Levon Petrosyan

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;
}

您可以使用view.setDrawable获取可绘制对象,并将其应用于任何视图。
对于Lollipop+设备,您将获得涟漪效果,否则它将更改视图的颜色。


这很好。我认为一个缺点是,当应用于不支持Lollipop的设备时,它可能会失去圆角,因为StateListDrawable中的ColorDrawable没有形状。 我们可以提供有形状的彩色ShapeDrawable来保持形状。但也许这会开始变得有点沉重。在这种情况下,另一种解决方案是创建一个定制的“ColorStateDrawable”,其中包含一个“setColor”方法,避免您重新实例化所有需要StateListDrawable的对象。 - John
在这里找到了类似的东西:https://github.com/gabrielemariotti/colorpickercollection/blob/master/ColorPicker/src/it/gmariotti/android/example/colorpicker/calendarstock/ColorStateDrawable.java - John
1
"normalColor"不起作用!它总是透明的。 - ucMedia

2
基本上,您需要创建一个新的RippleDrawable对象。对于Android 5.0以下的设备,您需要一个StateListDrawable(正如其他人已经建议的那样)。我写了一个相当丰富的GIST,其中包含与Drawables和着色相关的许多有用方法:https://gist.github.com/milosmns/6566ca9e3b756d922aa5 最有可能您将要使用该单例中的#getBackgroundDrawable()

这真的太庞大了,我不确定在这里发布十个五十行方法的链是否相关。这是我的要点,所以它被有意地保持着 :) - milosmns

0

你可以将其分配为前景,在 Kotlin 中是:

view.foreground = ContextCompat.getDrawable(context, R.drawable.rippleRes)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接