Android: клонирование отрисовки для создания StateListDrawable с фильтрами

Я пытаюсь сделать общую каркасную функцию, которая делает любой Drawable выделенным при нажатии / фокусировке / выделении / и т.д.

Моя функция берет Drawable и возвращает StateListDrawable, где состоянием по умолчанию является сам Drawable, а состояние для android.R.attr.state_pressed то же самое рисовать, только с фильтром, примененным с помощью setColorFilter,

Моя проблема в том, что я не могу клонировать нарисованное и создать отдельный экземпляр с примененным фильтром. Вот что я пытаюсь достичь:

StateListDrawable makeHighlightable(Drawable drawable)
{
    StateListDrawable res = new StateListDrawable();

    Drawable clone = drawable.clone(); // how do I do this??

    clone.setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
    res.addState(new int[] {android.R.attr.state_pressed}, clone);
    res.addState(new int[] { }, drawable);
    return res;
}

Если я не клонирую, тогда фильтр явно применяется к обоим состояниям. Я пытался играть с mutate() но это не помогает..

Есть идеи?

Обновить:

Принятый ответ действительно клонирует притягивающий. Это не помогло мне, хотя, потому что моя общая функция не работает из-за другой проблемы. Кажется, что когда вы добавляете drawable в StateList, он теряет все свои фильтры.

5 ответов

Решение

Попробуйте следующее:

Drawable clone = drawable.getConstantState().newDrawable();

Если вы примените фильтр / etc к рисованному объекту, созданному с getConstantState().newDrawable() тогда все экземпляры этого drawable будут также изменены, так как drawables используют constantState как кеш!

Так что, если вы окрашиваете круг, используя цветной фильтр и newDrawable(), вы измените цвет всех кругов.

Если вы хотите, чтобы это рисование можно было обновлять, не затрагивая другие экземпляры, тогда вы должны изменить это существующее постоянное состояние.

// To make a drawable use a separate constant state
drawable.mutate()

Для хорошего объяснения смотрите:

http://www.curious-creature.org/2009/05/02/drawable-mutations/

http://developer.android.com/reference/android/graphics/drawable/Drawable.html

Это то, что работает для меня.

Drawable clone = drawable.getConstantState().newDrawable().mutate();

Это мое решение, основанное на этом вопросе.

Идея в том, что ImageView получает цветной фильтр, когда пользователь прикасается к нему, и цветной фильтр удаляется, когда пользователь перестает прикасаться к нему. В памяти только 1 рисованный / растровый образ, поэтому не нужно тратить его впустую. Работает как надо.

class PressedEffectStateListDrawable extends StateListDrawable {

    private int selectionColor;

    public PressedEffectStateListDrawable(Drawable drawable, int selectionColor) {
        super();
        this.selectionColor = selectionColor;
        addState(new int[] { android.R.attr.state_pressed }, drawable);
        addState(new int[] {}, drawable);
    }

    @Override
    protected boolean onStateChange(int[] states) {
        boolean isStatePressedInArray = false;
        for (int state : states) {
            if (state == android.R.attr.state_pressed) {
                isStatePressedInArray = true;
            }
        }
        if (isStatePressedInArray) {
            super.setColorFilter(selectionColor, PorterDuff.Mode.MULTIPLY);
        } else {
            super.clearColorFilter();
        }
        return super.onStateChange(states);
    }

    @Override
    public boolean isStateful() {
        return true;
    }
}

использование:

Drawable drawable = new FastBitmapDrawable(bm);
imageView.setImageDrawable(new PressedEffectStateListDrawable(drawable, 0xFF33b5e5));

Я ответил на связанный вопрос здесь

По сути, кажется, что StateListDrawables действительно теряют свои фильтры. Я создал новый BitmapDrawale из измененной копии растрового изображения, которое изначально хотел использовать.

Получите возможность клонирования с помощью newDrawable() но убедитесь, что он изменяемый, иначе эффект клонирования исчезнет, ​​я использовал эти несколько строк кода, и он работает должным образом. getConstantState() может иметь значение null, как это предлагается аннотацией, поэтому обработайте это RunTimeException при клонировании drawable.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Drawable clone = drawable.mutate().getConstantState().newDrawable().mutate();

в случае getConstantState() возвращается null.

Другие вопросы по тегам