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

91

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

Моя функция принимает Drawable и возвращает StateListDrawable, где состояние по умолчанию - это сам Drawable, а состояние for android.R.attr.state_pressedтакое же drawable, только с примененным фильтром с использованием 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()но это не помогает ..

Любые идеи?

Обновить:

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

талькол
источник
Привет, вы нашли решение проблемы потери фильтров в чертежах? У меня возникла такая же проблема :( В итоге я создал другое изображение из исходного изображения путем клонирования растрового изображения и применения фильтра попиксельно. Да, это неэффективно, но у меня есть только несколько небольших изображений, обработанных один раз.
port443
Я не смог решить эту проблему с помощью StateListDrawable, но если вы не используете StateListDrawable и по-прежнему теряете фильтры, убедитесь, что ваши растровые изображения изменяемы. Есть хорошие вопросы по теме: stackoverflow.com/questions/5499637/… , также я обнаружил, что LightingColorFilter работает там, где PorterDuff не работает .. люблю этот андроид :)
talkol
отличный ответ по этой ссылке stackoverflow.com/questions/10889415/…
Алан
Есть похожий побочный эффект, вызванный ImageView.setImageDrawable, который я смог обойти благодаря принятому ответу.
Джулио Пьянкастелли,
Я пытаюсь сделать то же самое, и это как-то работает, как ожидалось, ColorFilter не потерялся ... Разница в том, что я изменил drawable.
Генри

Ответы:

163

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

Drawable clone = drawable.getConstantState().newDrawable();
Флавио
источник
1
Благодарность! Этот метод, похоже, успешно клонирует рисованный объект. Функция, которую я пытался написать, не работает .. Похоже, что когда drawable вставляется в StateList, он теряет свои фильтры :(
talkol 03
3
+1 за помощь в исправлении очень странной ошибки в MapView, когда повторное использование Drawable из ItemizedOverlay в AlertDialog приводило к перемещению ItemizedOverlay при срабатывании. Создание нового экземпляра Drawable устранило проблему.
kskjon 02
9
Сделайте, чтобы работало правильно, если мы попробуем использовать метод setAlpha. В этом случае оба рисунка изменяют растровое изображение. Затем я получаю первый доступный как: getResources (). GetDrawable (), второй как: getResources (). GetDrawable (). Mutate ().
Юра Шинкарев
Большое спасибо, он устранил проблему, с которой я столкнулся, когда применил ограничивающую функцию из API Mapsforge. Теперь я могу успешно использовать чертежи везде!
xarlymg89
18
@Flavio - Я пробовал это с цветным фильтром, но он раскрасил все экземпляры моего drawable! Оказывается, вам нужно использовать .mutate()(см. Мой ответ).
Питер Айтай
106

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

Итак, если вы раскрасите круг с помощью цветового фильтра и символа a 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#mutate ()

Питер Айтай
источник
Фактически, mutate () возвращает тот же самый экземпляр, но его внутреннее состояние изменяется, поэтому применение цветового фильтра не повлияет на другие экземпляры. Можете ли вы просмотреть и исправить свой ответ?
clemp6r 01
1
@ clemp6r, если вы не используете mutate всех экземпляров изменения цвета - вам нужно вызвать mutate, чтобы изменить только цвет клона
Питер Аджтай
2
Проверьте ссылку на API («Сделать эту возможность рисования изменяемой. - Возвращает эту возможность рисования») и исходный код («вернуть это»). Вызов mutate () требуется, но возвращаемый экземпляр остается прежним. Это не создает клон, это только изменяет внутреннее состояние вытягиваемого экземпляра, чтобы можно было изменять его, не затрагивая другие экземпляры того же самого вытягиваемого объекта.
clemp6r 02
Ну я не знаю , о вопросе, но этот ответ делает точную вещь , мне нужно ... тонн урана
Эврен Ozturk
1
Это лучшие ссылки, которые вы дали для справки
Ашок Варма
16

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

Drawable clone = drawable.getConstantState().newDrawable().mutate();
Янру Би
источник
ДА, я не знаю, ПОЧЕМУ, но только эта комбинация newDrawable () и mutate () работает для меня, любой другой одиночный mutate () или единственный newDrawable () у меня не работает правильно
Michał Ziobro
12

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

Идея состоит в том, что 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));
Малахиас
источник
У меня тоже работает! Интересное решение, спасибо!) PS андроид отстой, такой плохой не работает API :(
Антон Кизема
Я думаю, что это лучшее решение для устранения ошибок в (StateListDrawable + BitmapDrawable)!
Xavier.S 02
1

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

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

Куно
источник
0
Drawable clone = drawable.mutate().getConstantState().newDrawable().mutate();

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

Мартин Ван
источник
0

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

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Кишан Донга
источник