Android: clonación de un elemento de dibujo para hacer un StateListDrawable con filtros


91

Estoy tratando de hacer una función marco general que hace que cualquier Disponibles se resaltarán cuando se pulsa / centrado / seleccionado / etc .

Mi función toma Drawable y devuelve StateListDrawable, donde el estado predeterminado es Drawable en sí mismo, y el estado para android.R.attr.state_pressedes el mismo dibujable, solo que con un filtro aplicado usando setColorFilter.

Mi problema es que no puedo clonar el dibujable y hacer 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 clono, obviamente el filtro se aplica a ambos estados. Intenté jugar con mutate()pero no ayuda.

¿Algunas ideas?

Actualizar:

De hecho, la respuesta aceptada clona un dibujable. Sin embargo, no me ayudó porque mi función general falla en un problema diferente. Parece que cuando agrega un elemento de diseño a StateList, pierde todos sus filtros.


Hola, ¿encontraste una solución para los drawables que pierden los filtros? Me encontré con el mismo problema :( Terminé generando otra imagen a partir de la imagen de origen clonando el mapa de bits y aplicando el filtro píxel por píxel. Sí, esto es ineficiente, pero solo tengo un montón de imágenes pequeñas procesadas una vez.
port443

No pude resolverlo con StateListDrawable, pero si no está usando StateListDrawable y aún pierde sus filtros, asegúrese de que sus mapas de bits sean mutables. Hay buenas preguntas relacionadas: stackoverflow.com/questions/5499637/… , también he descubierto que LightingColorFilter funciona en lugares donde falla PorterDuff .. me encanta este android :)
talkol

una gran respuesta en este enlace stackoverflow.com/questions/10889415/…
Alan

Hay un efecto secundario similar provocado por ImageView.setImageDrawable, que pude evitar gracias a la respuesta aceptada.
Giulio Piancastelli

Estoy tratando de hacer lo mismo y funciona como se esperaba de alguna manera, el ColorFilter no se perdió ... La diferencia es que cambié el dibujable.
Henry

Respuestas:


162

Intente lo siguiente:

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

1
¡Gracias! Este método parece clonar un dibujable con éxito. La función que estaba tratando de escribir no funciona. Parece que cuando se inserta un elemento de diseño en una StateList pierde sus filtros :(
talkol

3
+1 por ayudarme a corregir un error muy extraño en MapView donde la reutilización de Drawable de ItemizedOverlay en un AlertDialog hizo que ItemizedOverlay se moviera cuando se activaba. Hacer una nueva instancia del Drawable solucionó el problema.
kskjon

9
Hazlo para que funcione correctamente, si intentamos usar el método setAlpha. En este caso, ambos mapas de bits de cambio dibujables. Luego obtengo el primer dibujable como: getResources (). GetDrawable (), el segundo como: getResources (). GetDrawable (). Mutate ().
Yura Shinkarev

Muchas gracias, solucionó el problema que tenía cuando apliqué una función de delimitación, de la API Mapsforge. ¡Ahora puedo usar con éxito los dibujables en todas partes!
xarlymg89

18
@Flavio: probé esto con un filtro de color, ¡pero coloreó todas las instancias de mi dibujante! Resulta que parece que tienes que usar .mutate()(mira mi respuesta).
Peter Ajtai

106

Si aplica un filtro / etc a un elemento de diseño creado con getConstantState().newDrawable(), todas las instancias de ese elemento de diseño también se cambiarán, ya que los elementos de diseño utilizan elconstantState como caché!

Entonces, si coloreas un círculo con un filtro de color y un newDrawable() , cambiarás el color de todos los círculos.

Si desea que este elemento de diseño sea actualizable sin afectar a otras instancias, entonces debe mutar ese estado constante existente.

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

Para obtener una buena explicación, consulte:

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

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


De hecho, mutate () devuelve exactamente la misma instancia, pero su estado interno cambia, por lo que la aplicación de un filtro de color no afectará a otras instancias. ¿Puedes revisar y corregir tu respuesta?
clemp6r

1
@ clemp6r si no usa mutate todas las instancias del cambio de color, debe llamar a mutate para cambiar solo el color del clon
Peter Ajtai

2
Compruebe la referencia de API ("Hacer que este elemento de diseño sea mutable. - Devuelve este elemento de diseño") y el código fuente ("devolver esto"). Se requiere llamar a mutate (), pero la instancia devuelta es la misma. Esto no crea un clon, esto solo cambia el estado interno de la instancia dibujable para permitir modificarlo sin afectar otras instancias del mismo dibujable.
clemp6r

Bueno, no sé sobre la pregunta, pero esta respuesta hace exactamente lo que necesitaba ... tU
Evren Ozturk

1
Esos son los mejores enlaces, los que dio como referencia
Ashok Varma

15

Esto es lo que me funciona.

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

SÍ, no sé POR QUÉ, pero solo esta combinación newDrawable () y mutate () funciona para mí, cualquier otro mutate () o newDrawable () único no funciona correctamente para mí
Michał Ziobro

12

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

La idea es que ImageViewobtenga un filtro de color cuando el usuario lo toque, y el filtro de color se elimine cuando el usuario deja de tocarlo. Solo hay 1 mapa de bits / dibujable en la memoria, por lo que no es necesario 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));

¡Funciona para mí también! ¡Esa es una solución interesante, gracias!) PS Android apesta, tan mal API que no funciona correctamente :(
Anton Kizema

¡Creo que esta es la mejor solución para resolver errores en (StateListDrawable + BitmapDrawable)!
Xavier.S

1

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.


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

en caso de getConstantState()devoluciones null.


0

Obtenga clon dibujable usando newDrawable()pero asegúrese de que sea mutable, de lo contrario su efecto de clonación desaparecerá, utilicé estas pocas líneas de código y está funcionando como se esperaba. getConstantState()puede ser nulo como lo sugiere la anotación, así que maneje esta RunTimeException mientras clona el elemento de dibujo.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.