2011-11-02 11 views
75

Sto cercando di creare una funzione framework generale che renda ogni Drawable evidenziato quando premuto/focused/selezionato/etc.Android: clonazione di un drawable per creare un oggetto StateListDrawable con i filtri

La mia funzione accetta un Drawable e restituisce StateListDrawable, dove lo stato predefinito è Drawable stesso e lo stato per android.R.attr.state_pressed è lo stesso drawable, solo con un filtro applicato utilizzando setColorFilter.

Il mio problema è che non posso clonare il drawable e creare un'istanza separata con il filtro applicato. Ecco cosa sto cercando di ottenere:

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

Se non clone, il filtro viene ovviamente applicato a entrambi gli stati. Ho provato a giocare con mutate() ma non aiuta ..

Qualche idea?

Aggiornamento:

La risposta accettata cloni davvero un disegnabile. Non mi ha aiutato però perché la mia funzione generale non riesce su un problema diverso. Sembra che quando si aggiunge un drawable a StateList, questo perde tutti i suoi filtri.

+0

Ciao, hai trovato una soluzione per i drawable che perdevano i filtri? Ho incontrato lo stesso problema :(Ho finito per generare altra immagine dall'immagine sorgente clonando Bitmap e applicando il filtro pixel per pixel. Sì, questo è inefficiente, ma ho solo un po 'di piccole immagini elaborate una volta .. – port443

+0

I non è stato possibile risolverlo con StateListDrawable, ma se non stai usando StateListDrawable e stai ancora perdendo i tuoi filtri, assicurati che le tue bitmap siano mutevoli. Ci sono buone domande correlate: http://stackoverflow.com/questions/5499637/drawable-setcolorfilter -not-working-on-android-2-1, inoltre ho scoperto che LightingColorFilter funziona in posti dove PorterDuff fallisce .. lovin questo android :) – talkol

+0

un'ottima risposta su questo link http://stackoverflow.com/questions/ 10889415/aggiunta-un-colore-filtro-a-un-disegnabile-cambia-tutti-pulsanti-usando-lo stesso-disegnabile – Alan

risposta

138

provare quanto segue:

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

Grazie! Questo metodo sembra clonare un drawable con successo. La funzione che stavo cercando di scrivere però non funziona .. Sembra che quando un drawable viene inserito in una StateList perde i suoi filtri :( – talkol

+3

+1 per aiutarmi a correggere un errore molto strano in MapView dove riutilizzare un Drawable da l'oggetto ItemizedOverlay in AlertDialog ha fatto la mossa ItemizedOverlay quando attivato Fare una nuova istanza del Drawable ha risolto il problema – kskjon

+8

Do per funzionare correttamente, se proviamo ad usare il metodo setAlpha .In questo caso entrambi i bitmap modificabili prima drawable come: getResources(). getDrawable(), second as: getResources(). getDrawable(). mutate(). –

1

ho risposto a una domanda relativa here

In pratica sembra che StateListDrawables anzi perdono i loro filtri. Ho creato un nuovo BitmapDrawale da una copia modificata della Bitmap che volevo originariamente utilizzare.

86

Se si applica un filtro/etc a un drawable creato con getConstantState().newDrawable(), verranno modificate anche tutte le istanze di tale drawable, poiché i drawable utilizzano lo constantState come cache!

Quindi, se si colora un cerchio con filtro colore e newDrawable, si cambierà il colore di tutti i cerchi.

Se si desidera ottenere questo drawable aggiornabile senza interessare altre istanze, è necessario modificare lo stato costante esistente.

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

Per una buona spiegazione, vedere:

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

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

+0

Infatti, mutate() restituisce la stessa identica istanza, ma il suo stato interno viene modificato, pertanto l'applicazione di un filtro colore non avrà alcun impatto sulle altre istanze. Riesci a rivedere e correggere la tua risposta? – clemp6r

+1

@ clemp6r se non si utilizzano tutte le mutazioni delle variazioni di colore del mutamento - è necessario chiamare mutate per modificare solo il colore del clone –

+1

Controllare il [riferimento API] (http://developer.android.com /reference/android/graphics/drawable/Drawable.html#mutate()) ("Rendi questa variabile mutevole. - Restituisce questo drawable") e il [codice sorgente] (http://grepcode.com/file/repo1.maven .org/maven2/org.robolectric/android-all/4.2.2_r1.2-robolectric-0/android/graphics/drawable/BitmapDrawable.java # 426) ("restituisci questo"). È richiesta la chiamata a mutate(), ma l'istanza restituita è la stessa. Questo non crea un clone, questo modifica solo lo stato interno dell'istanza drawable per consentire di modificarlo senza influenzare altre istanze dello stesso drawable. – clemp6r

10

Questa è la mia soluzione, sulla base di questo codice: https://stackoverflow.com/a/6071813/2075875. L'idea è che ImageView ottenga il filtro colore quando l'utente lo tocca e il filtro colore viene rimosso quando l'utente smette di toccarlo. Solo 1 drawable/bitmap è in memoria, quindi non è necessario sprecarlo. Funziona come dovrebbe

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

utilizzo:

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

Funziona anche per me, grazie! – Mikhail

+0

Funziona anche per me! Questa è una soluzione interessante, grazie!) P.S.Android fa schifo, così male API non funzionante correttamente :( –

+0

Penso che questa sia la soluzione migliore di gran lunga per risolvere i bug in (StateListDrawable + BitmapDrawable)! –

6

Questo è ciò che funziona per me.

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