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()але це не допомагає ..

Будь-які ідеї?

Оновлення:

Прийнята відповідь справді клонує малюнок. Це мені не допомогло, оскільки моя загальна функція не працює з іншою проблемою. Здається, коли ви додаєте малюнок до списку стану, він втрачає всі свої фільтри.


Привіт, ти знайшов рішення для втрати графічних матеріалів, що втрачають фільтри? Я зіткнувся з тією ж проблемою :( Я в кінцевому підсумку генерував інше зображення із вихідного зображення, клонуючи Bitmap і застосовуючи фільтр піксель за пікселем. Так, це неефективно, але я маю лише купу невеликих зображень, оброблених один раз.
port443

Я не міг вирішити це за допомогою StateListDrawable, але якщо ви не використовуєте StateListDrawable і все ще втрачаєте фільтри, переконайтеся, що ваші растрові зображення можна змінювати. Є хороші пов’язані запитання: stackoverflow.com/questions/5499637/… , також я виявив, що LightingColorFilter працює в місцях, де PorterDuff не працює .. люблю цього андроїда :)
talkol

чудова відповідь за цим посиланням stackoverflow.com/questions/10889415/…
Алан

Існує подібний побічний ефект, який спричинив і ImageView.setImageDrawableя, який я зміг обійти завдяки прийнятій відповіді.
Джуліо Піанкастеллі

Я намагаюся зробити те саме, і це працює якось, як очікувалось, ColorFilter не загубився ... Різниця в тому, що я мутував малювальне.
Генрі

Відповіді:


162

Спробуйте наступне:

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

1
Дякую! Здається, цей метод успішно клонує малювальний файл. Функція, яку я намагався написати, не працює. Здається, що коли файл, який можна малювати, вставляється в StateList, він втрачає свої фільтри :(
talkol

3
+1 за допомогу у виправленні дуже дивної помилки в MapView, коли повторне використання Drawable з ItemizedOverlay в AlertDialog змусило ItemizedOverlay рухатися при спрацьовуванні. Створення нового екземпляра Drawable вирішило проблему.
kskjon

9
Зробити, щоб працювали правильно, якщо ми спробуємо використовувати метод setAlpha. У цьому випадку обидва графічні файли змінюють растрове зображення. Тоді я отримую перший drawable як: getResources (). GetDrawable (), другий як: getResources (). GetDrawable (). Mutate ().
Юра Шинкарев

Велике спасибі, це вирішило проблему, яку я мав, коли застосовував обмежувальну функцію, з API Mapsforge. Тепер я можу успішно використовувати скрізь скрізь!
xarlymg89

18
@Flavio - Я спробував це з кольоровим фільтром, але це забарвило всі екземпляри мого малювання! Виявляється, схоже, що ти повинен використовувати .mutate()(див. Мою відповідь).
Peter Ajtai

106

Якщо ви застосуєте фільтр / і т.д. до файлу, що малюється, створеного за допомогою, 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

1
@ clemp6r, якщо ви не використовуєте мутацію всіх випадків зміни кольору - вам потрібно викликати мутацію, щоб змінити лише колір клону
Пітер Айтай

2
Перевірте посилання на API ("Зробити змінне для малювання. - Повертає для малювання") та вихідний код ("повернути це"). Виклик mutate () необхідний, але повернутий екземпляр той самий. Це не створює клон, це лише змінює внутрішній стан екземпляра, що малюється, щоб дозволити його модифікацію, не впливаючи на інші екземпляри того самого малюваного.
clemp6r

Ну, я не знаю про питання, але ця відповідь робить саме те, що мені потрібно ... тУ
Еврен Озтюрк

1
Це найкращі посилання, посилання на які ви дали для довідки
Ашок Варма

15

Це те, що мені підходить.

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

ТАК Я не знаю ЧОМУ, але лише ця комбінація newDrawable () та mutate () працює для мене, будь-яка інша одинарна мутація () або одиночна newDrawable () не працюють для мене коректно
Міхал Зіобро,

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)!
Ксав'єр. 02

1

Я відповів на відповідне запитання тут

В основному здається, що StateListDrawables дійсно втрачає свої фільтри. Я створив новий BitmapDrawale із зміненої копії Bitmap, який спочатку хотів використовувати.


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

у разі getConstantState()повернення null.


0

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

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.