web-dev-qa-db-fra.com

Android: clonage d'un dessinable afin de créer un StateListDrawable avec des filtres

J'essaie de faire une fonction de cadre général qui rend tout Drawable mis en évidence lorsque pressé/concentré/sélectionné/etc.

Ma fonction prend un Drawable et retourne un StateListDrawable, où l'état par défaut est le Drawable lui-même, et l'état pour Android.R.attr.state_pressed Est le même drawable, juste avec un filtre appliqué en utilisant setColorFilter.

Mon problème est que je ne peux pas cloner le dessinable et en faire une instance séparée avec le filtre appliqué. Voici ce que j'essaie de réaliser:

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 je ne clone pas, le filtre est évidemment appliqué aux deux états. J'ai essayé de jouer avec mutate() mais ça n'aide pas ..

Des idées?

Mise à jour:

La réponse acceptée clone en effet un dessinable. Cela ne m'a pas aidé car ma fonction générale échoue sur un problème différent. Il semble que lorsque vous ajoutez un dessinable à une StateList, il perd tous ses filtres.

86
talkol

Essayez ce qui suit:

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

Si vous appliquez un filtre/etc à un dessinable créé avec getConstantState().newDrawable() alors toutes les instances de ce dessinable seront également modifiées, car les dessinables utilisent le constantState comme cache!

Donc, si vous coloriez un cercle à l'aide d'un filtre de couleur et d'une newDrawable(), vous changerez la couleur de tous les cercles.

Si vous souhaitez rendre cette mise à jour dessinable sans affecter les autres instances, vous devez alors muter cet état constant existant.

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

Pour une bonne explication, voir:

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

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

98
Peter Ajtai

C'est ce qui fonctionne pour moi.

Drawable clone = drawable.getConstantState().newDrawable().mutate();
14
Yanru Bi

Ceci est ma solution, basée sur cela question SO .

L'idée est que ImageView obtient un filtre de couleur lorsque l'utilisateur le touche et que le filtre de couleur est supprimé lorsque l'utilisateur cesse de le toucher. Un seul bitmap/bitmap est en mémoire, donc pas besoin de le gaspiller. Cela fonctionne comme il se doit.

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

usage:

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

J'ai répondu à une question connexe ici

Fondamentalement, il semble que StateListDrawables perd en effet ses filtres. J'ai créé un nouveau BitmapDrawale à partir d'une copie modifiée du Bitmap que je voulais utiliser à l'origine.

1
Kuno