Android: клонирование рисунка, чтобы сделать StateListDrawable с фильтрами

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

Моя функция принимает 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(), но это не помогает..

Любые идеи?

Update:

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

Ответ 1

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

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

Ответ 2

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

Итак, если вы покрасьте круг с цветовым фильтром и newDrawable, вы измените цвет всех кругов.

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

// 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#mutate()

Ответ 3

Это мое решение, основанное на этом коде: fooobar.com/questions/78311/.... Идея заключается в том, что 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));

Ответ 4

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

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

Ответ 5

Я ответил на соответствующий вопрос здесь

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