如何触发Android Lollipop上的涟漪效应,在视图内的特定位置,而不触发触摸事件?

and*_*per 21 android rippledrawable android-5.0-lollipop

这是一个简短的问题:

假设我有一个ViewRippleDrawable作为背景.

是否有一种简单的方法可以触发特定位置的纹波而不会触发任何触摸或点击事件?

Xav*_*ler 30

就在这里!为了以编程方式触发纹波,您必须设置RippleDrawablewith 的状态setState().呼叫setVisible()不会工作!


解决方案

要显示纹波,您必须同时将状态设置为按下并启用:

rippleDrawable.setState(new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled });
Run Code Online (Sandbox Code Playgroud)

只要设置了这些状态,就会显示纹波.如果要再次隐藏纹波,请将状态设置为空int[]:

rippleDrawable.setState(new int[] {  });
Run Code Online (Sandbox Code Playgroud)

您可以通过调用设置波纹发出的点setHotspot().


这个怎么运作

我已经调试了很多并且研究了RippleDrawable上下的源代码,直到我意识到纹波实际上是被触发的onStateChange().调用setVisible()没有任何影响,也不会导致实际出现任何涟漪.

源代码的相关部分RippleDrawable是这样的:

@Override
protected boolean onStateChange(int[] stateSet) {
    final boolean changed = super.onStateChange(stateSet);

    boolean enabled = false;
    boolean pressed = false;
    boolean focused = false;

    for (int state : stateSet) {
        if (state == R.attr.state_enabled) {
            enabled = true;
        }
        if (state == R.attr.state_focused) {
            focused = true;
        }
        if (state == R.attr.state_pressed) {
            pressed = true;
        }
    }

    setRippleActive(enabled && pressed);
    setBackgroundActive(focused || (enabled && pressed));

    return changed;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,如果同时设置了enabled和pressed属性,则会激活纹波和背景,并显示纹波.此外,只要您设置了聚焦状态,背景也将被激活.通过这种方式,您可以触发纹波并使背景颜色独立.

如果您有兴趣,可以查看RippleDrawable 此处的完整源代码.


Luk*_*uke 16

我将@Xaver Kapeller和@Nikola Despotoski的答案合并/组合在上面:

protected void forceRippleAnimation(View view)
{
    Drawable background = view.getBackground();

    if(Build.VERSION.SDK_INT >= 21 && background instanceof RippleDrawable)
    {
        final RippleDrawable rippleDrawable = (RippleDrawable) background;

        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(new int[]{});
            }
        }, 200);
    }
}
Run Code Online (Sandbox Code Playgroud)

要以编程方式强制对命令施加涟漪效应,只需调用forceRippleAnimation(),将您想要作为参数的视图传递给它.


Nik*_*ski 7

首先,您需要从View中获取drawable.

private void forceRippleAnimation(View v, float x, float y){
   Drawable background = v.getBackground();
   if(background instanceof RippleDrawable){
     RippleDrawable ripple = (RippleDrawable)background;
     ripple.setHotspot(x, y);
     ripple.setVisible (true, true);
   }

}
Run Code Online (Sandbox Code Playgroud)

方法setHotspot(x,y);用于设置纹波动画开始的位置,否则如果未设置,RippleDrawable将采用Rect它所在的位置(即Rect视图设置为背景的位置)并从中心开始产生连锁效果.

setVisible(true, true) 将使drawable可见,最后一个参数将强制动画,无论当前可绘制状态如何.


ypr*_*sto 7

以下是Nikola的setHotSpot()和/sf/answers/1779083001/的组合

private void forceRipple(View view, int x, int y) {
    Drawable background = view.getBackground();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && background instanceof RippleDrawable) {
        background.setHotspot(x, y);
    }
    view.setPressed(true);
    // For a quick ripple, you can immediately set false.
    view.setPressed(false);
}
Run Code Online (Sandbox Code Playgroud)