Android: klonowanie elementu do rysowania w celu utworzenia StateListDrawable z filtrami


91

Próbuję stworzyć ogólną funkcję ramową, która sprawia, że ​​każdy Drawable zostanie podświetlony po naciśnięciu / skupieniu / wybraniu / itp .

Moja funkcja przyjmuje Drawable i zwraca StateListDrawable, gdzie domyślnym stanem jest sam Drawable, a stan dla android.R.attr.state_pressedjest taki sam do rysowania, tylko z filtrem zastosowanym przy użyciu setColorFilter.

Mój problem polega na tym, że nie mogę sklonować tego, co można narysować, i utworzyć oddzielną instancję z zastosowanym filtrem. Oto, co próbuję osiągnąć:

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

Jeśli nie klonuję, filtr jest oczywiście stosowany do obu stanów. Próbowałem się bawić, mutate()ale to nie pomaga ...

Jakieś pomysły?

Aktualizacja:

Zaakceptowana odpowiedź rzeczywiście klonuje element do wyciągnięcia. Nie pomogło mi to jednak, ponieważ moja ogólna funkcja zawodzi w innym problemie. Wygląda na to, że kiedy dodajesz drawable do StateList, traci wszystkie swoje filtry.


Cześć, czy udało Ci się znaleźć rozwiązanie problemu utraty filtrów? Napotkałem ten sam problem :( Skończyło się na tym, że wygenerowałem inny obraz z obrazu źródłowego, klonując Bitmapę i stosując filtr piksel po pikselu. Tak, jest to nieefektywne, ale mam tylko kilka małych obrazów przetworzonych raz.
port443

Nie mogłem tego rozwiązać za pomocą StateListDrawable, ale jeśli nie używasz StateListDrawable i nadal tracisz filtry, upewnij się, że mapy bitowe są mutowalne. Są dobre powiązane pytania: stackoverflow.com/questions/5499637/… , odkryłem też, że LightingColorFilter działa w miejscach, w których PorterDuff zawodzi .. uwielbiam tego Androida :)
talkol

świetna odpowiedź na ten link stackoverflow.com/questions/10889415/…
Alan

Jest wywoływany podobny efekt uboczny ImageView.setImageDrawable, który udało mi się obejść dzięki zaakceptowanej odpowiedzi.
Giulio Piancastelli,

Próbuję zrobić to samo i jakoś działa zgodnie z oczekiwaniami, ColorFilter się nie zgubił ... Różnica polega na tym, że zmutowałem element do rysowania.
Henry

Odpowiedzi:


163

Spróbuj wykonać następujące czynności:

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

1
Dzięki! Wydaje się, że ta metoda pomyślnie klonuje plik do rysowania. Funkcja, którą próbowałem napisać, nie działa. Wygląda na to, że kiedy element rysunkowy jest wstawiony do StateList, traci swoje filtry :(
talkol

3
+1 za pomoc w naprawieniu bardzo dziwnego błędu w MapView, gdzie ponowne użycie Drawable z ItemizedOverlay w AlertDialog spowodowało, że ItemizedOverlay poruszyło się po uruchomieniu. Utworzenie nowej instancji Drawable rozwiązało problem.
kskjon

9
Zrób, aby działało poprawnie, jeśli spróbujemy użyć metody setAlpha. W tym przypadku oba rysowalne zmieniają bitmapę. Następnie otrzymuję pierwszy do rysowania jako: getResources (). GetDrawable (), drugi jako: getResources (). GetDrawable (). Mutate ().
Yura Shinkarev

Wielkie dzięki, rozwiązałem problem, który miałem, gdy zastosowałem funkcję ograniczającą z API Mapsforge. Teraz mogę z powodzeniem używać rysunków wszędzie!
xarlymg89

18
@Flavio - próbowałem tego z filtrem kolorów, ale zabarwiło wszystkie instancje do rysowania! Wygląda na to, że musisz użyć .mutate()(zobacz moją odpowiedź).
Peter Ajtai

106

Jeśli zastosujesz filtr / etc do elementu do rysowania utworzonego za pomocą, getConstantState().newDrawable()wszystkie wystąpienia tego elementu do rysowania również zostaną zmienione, ponieważ elementy do rysowania używają constantStatejako pamięci podręcznej!

Więc jeśli pokolorujesz okrąg za pomocą filtra kolorów i a newDrawable(), zmienisz kolor wszystkich okręgów.

Jeśli chcesz, aby ten możliwy do rysowania był aktualizowany bez wpływu na inne instancje, musisz zmodyfikować ten istniejący stały stan.

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

Aby uzyskać dobre wyjaśnienie, zobacz:

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

http://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate ()


W rzeczywistości mutate () zwraca dokładnie to samo wystąpienie, ale jego stan wewnętrzny ulega zmianie, więc zastosowanie filtru koloru nie wpłynie na inne wystąpienia. Czy możesz przejrzeć i poprawić swoją odpowiedź?
clemp6r

1
@ clemp6r jeśli nie używasz mutate wszystkich instancji zmiany koloru - musisz wywołać mutate, aby zmienić tylko kolor klonu
Peter Ajtai

2
Sprawdź odwołanie API ("Zrób to rysowalne mutowalne. - Zwraca to rysowalne") i kod źródłowy ("zwróć to"). Wywołanie mutate () jest wymagane, ale zwracana instancja jest taka sama. Nie tworzy to klonu, a jedynie zmienia stan wewnętrzny instancji do rysowania, aby umożliwić modyfikowanie jej bez wpływu na inne instancje tego samego elementu do rysowania.
clemp6r

Cóż, nie wiem o pytaniu, ale ta odpowiedź robi dokładnie to, czego potrzebowałem ... tU
Evren Ozturk

1
To są najlepsze linki, które podałeś jako odniesienie
Ashok Varma

16

To właśnie działa dla mnie.

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

TAK Nie wiem DLACZEGO ale tylko ta kombinacja newDrawable () i mutate () działa u mnie jakikolwiek inny pojedynczy mutate () lub pojedynczy newDrawable () nie działa poprawnie u mnie
Michał Ziobro

12

To jest moje rozwiązanie oparte na tym pytaniu SO .

Chodzi o to, że ImageViewfiltr koloru jest pobierany, gdy użytkownik go dotknie, a filtr koloru jest usuwany, gdy użytkownik przestaje go dotykać. W pamięci jest tylko 1 rysowalna / bitmapa, więc nie ma potrzeby jej marnować. Działa tak, jak powinno.

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

stosowanie:

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

U mnie też działa! To ciekawe rozwiązanie, dzięki!) PS android jest do bani, takie złe, źle działające API :(
Anton Kizema

Myślę, że jest to zdecydowanie najlepsze rozwiązanie do rozwiązywania błędów w (StateListDrawable + BitmapDrawable)!
Xavier.S

1

I odpowiedział powiązane pytanie tutaj

Zasadniczo wygląda na to, że StateListDrawables rzeczywiście tracą swoje filtry. Utworzyłem nowy BitmapDrawale ze zmienionej kopii Bitmapy, której pierwotnie chciałem użyć.


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

w przypadku getConstantState()zwrotów null.


0

Uzyskaj możliwość rysowania klonów za pomocą, newDrawable()ale upewnij się, że jest zmienny, w przeciwnym razie efekt klonowania zniknie. Użyłem tych kilku wierszy kodu i działa zgodnie z oczekiwaniami. getConstantState()może być null, jak sugeruje adnotacja, więc obsłuż ten wyjątek RunTimeException podczas klonowania do rysowania.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.