Как вызвать эффект пульсации на Android Lollipop в определенном месте в представлении, не вызывая прикосновения к событиям?

Это короткий вопрос:

Предположим, что у меня есть View с RippleDrawable в качестве фона.

Есть ли простой способ вызвать пульсацию из определенной позиции без запуска каких-либо событий касания или кликов?

Ответы

Ответ 1

Да, есть! Чтобы запрограммировать пульсацию программно, вам необходимо установить состояние RippleDrawable с помощью setState(). Вызов setVisible() работает НЕ!


Решение

Чтобы показать пульсацию, вы должны одновременно нажимать и включать состояние:

rippleDrawable.setState(new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled });

Шум будет отображаться до тех пор, пока будут установлены эти состояния. Если вы хотите скрыть пульсацию, снова установите состояние на пустой int[]:

rippleDrawable.setState(new int[] {  });

Вы можете установить точку, из которой вытекает пульсация, вызывая 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;
}

Как вы можете видеть, установлены ли как активированный, так и нажатый атрибут, будут активированы пульсация и фон, и будет отображаться пульсация. Кроме того, если вы установили сфокусированное состояние, фон также будет активирован. С этим вы можете вызвать пульсацию и независимо изменить цвет фона.

Если вам интересно, вы можете просмотреть весь исходный код RippleDrawable здесь.

Ответ 2

Я включил/объединил ответы от @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);
    }
}

Чтобы программно заставить эффект пульсации на команде, просто вызовите forceRippleAnimation(), передав представление, которое вы хотите пульсировать как параметр.

Ответ 3

Во-первых, вам нужно получить извлечение из представления.

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

}

Метод setHotspot(x,y); используется для установки с начала анимации пульсации, иначе, если не установлен, RippleDrawable будет принимать Rect, где он находится (т.е. Rect в представлении, где он установлен как фон) и начнет эффект пульсации от центра.

setVisible(true, true) сделает видимый и последний аргумент drawable, который заставит анимацию независимо от текущего текущего состояния.

Ответ 4

Вот сочетание Nikola setHotSpot() и fooobar.com/questions/332332/...

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

Ответ 5

Я использовал следующий Kotlin-вариант кода Люка, чтобы вручную показать и скрыть пульсации при перелистывании ячеек из RecyclerView:

fun View.showRipple() {
    if (Build.VERSION.SDK_INT >= 21 && background is RippleDrawable) {
        background.state = intArrayOf(android.R.attr.state_pressed, android.R.attr.state_enabled)
    }
}

fun View.hideRipple() {
    if (Build.VERSION.SDK_INT >= 21 && background is RippleDrawable) {
        background.state = intArrayOf()
    }
}