Android: clonando un dibujable para hacer un StateListDrawable con filtros

Intento crear una función de marco general que haga resaltar cualquier Drawable cuando se presiona / enfoca / selecciona / etc.

Mi función toma un Drawable y devuelve un StateListDrawable, donde el estado predeterminado es Drawable, y el estado de android.R.attr.state_pressed es el mismo dibujable, solo con un filtro aplicado usando setColorFilter .

Mi problema es que no puedo clonar el dibujable y crear una instancia separada con el filtro aplicado. Esto es lo que estoy tratando de lograr:

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

Si no lo hago, entonces el filtro obviamente se aplica a ambos estados. Intenté jugar con mutate() pero no ayuda …

¿Algunas ideas?

Actualizar:

La respuesta aceptada en verdad clona un dibujable. Aunque no me ayudó porque mi función general falla en un problema diferente. Parece que cuando agrega un drawable a StateList, pierde todos sus filtros.

Pruebe lo siguiente:

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

Si aplicas un filtro / etc a un getConstantState().newDrawable() creado con getConstantState().newDrawable() , todas las instancias de ese dibujable también se cambiarán, ¡ya que los dibujables usan constantState como caché!

Por lo tanto, si coloreas un círculo con un filtro de color y nuevoDrawable, cambiarás el color de todos los círculos.

Si desea hacer que este dibujable sea actualizable sin afectar otras instancias, entonces debe mutar ese estado constante existente.

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

Para una buena explicación, ver:

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

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

Esta es mi solución, basada en esta pregunta SO .

La idea es que ImageView obtenga un filtro de color cuando el usuario lo toque, y el filtro de color se elimina cuando el usuario deja de tocarlo. Solo 1 drawable / bitmap está en la memoria, por lo que no hay necesidad de desperdiciarlo. Funciona como deberia.

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

uso:

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

Esto es lo que funciona para mí.

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

Respondí una pregunta relacionada aquí

Básicamente, parece que StateListDrawables pierde sus filtros. Creé un nuevo BitmapDrawale a partir de una copia alterada del bitmap que originalmente quería usar.