150 votes

Comment ajouter un état de bouton personnalisé

Par exemple, le bouton par défaut a les dépendances suivantes entre ses états et ses images de fond :

Comment puis-je définir mon propre état personnalisé (quelque chose comme android:state_custom), afin de pouvoir ensuite l'utiliser pour changer dynamiquement l'apparence visuelle de mon bouton ?

0 votes

Je voulais des états supplémentaires pour une vue EditText pour déterminer quand deux cases de mot de passe correspondent pour afficher un petit coche.

297voto

Giorgio Barchiesi Points 2023

La solution indiquée par @(Ted Hopp) fonctionne, mais a besoin d'une petite correction : dans le sélecteur, les états des éléments ont besoin d'un préfixe "app :", sinon l'inflater ne reconnaîtra pas correctement l'espace de noms et échouera silencieusement ; du moins c'est ce qui m'arrive.

Permettez-moi de rapporter ici l'ensemble de la solution, avec quelques détails supplémentaires :

Tout d'abord, créez le fichier "res/values/attrs.xml" :

Ensuite, définissez votre classe personnalisée. Par exemple, cela peut être une classe "FoodButton", dérivée de la classe "Button". Vous devrez implémenter un constructeur ; implémentez celui-ci, qui semble être celui utilisé par l'inflater :

public FoodButton(Context context, AttributeSet attrs) {
    super(context, attrs);
}

En haut de la classe dérivée :

private static final int[] STATE_FRIED = {R.attr.state_fried};
private static final int[] STATE_BAKED = {R.attr.state_baked};

Aussi, vos variables d'état :

private boolean mIsFried = false;
private boolean mIsBaked = false;

Et quelques setters :

public void setFried(boolean isFried) {mIsFried = isFried;}
public void setBaked(boolean isBaked) {mIsBaked = isBaked;}

Ensuite, remplacez la fonction "onCreateDrawableState" :

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
    if (mIsFried) {
        mergeDrawableStates(drawableState, STATE_FRIED);
    }
    if (mIsBaked) {
        mergeDrawableStates(drawableState, STATE_BAKED);
    }
    return drawableState;
}

Enfin, la pièce la plus délicate de ce puzzle ; le sélecteur définissant le StateListDrawable que vous utiliserez comme arrière-plan pour votre widget. C'est le fichier "res/drawable/food_button.xml" :

Remarquez le préfixe "app :", alors qu'avec les états Android standard vous auriez utilisé le préfixe "android:". L'espace de noms XML est crucial pour une interprétation correcte par l'inflater et dépend du type de projet dans lequel vous ajoutez des attributs. S'il s'agit d'une application, remplacez com.mydomain.mypackage par le nom du package réel de votre application (nom de l'application exclu). S'il s'agit d'une bibliothèque, vous devez utiliser "http://schemas.android.com/apk/res-auto" (et utiliser Tools R17 ou ultérieur) ou vous obtiendrez des erreurs d'exécution.

Quelques notes :

  • Il semble que vous n'ayez pas besoin d'appeler la fonction "refreshDrawableState", du moins la solution fonctionne bien telle quelle, dans mon cas

  • Pour utiliser votre classe personnalisée dans un fichier de mise en page xml, vous devrez spécifier le nom entièrement qualifié (par exemple com.mydomain.mypackage.FoodButton)

  • Vous pouvez également mélanger les états standard (par exemple android:pressed, android:enabled, android:selected) avec des états personnalisés, afin de représenter des combinaisons d'états plus complexes

5 votes

Mise à jour : si la classe personnalisée dérive de TextView, plutôt que de Button, l'appel à refreshDrawableState semble être nécessaire, sinon l'apparence du widget n'est pas mise à jour. L'appel doit être placé dans les setters. Je n'ai pas essayé d'autres classes. Tests effectués sur un appareil froyo.

17 votes

Le refreshDrawableState est définitivement important. Je ne suis pas tout à fait sûr de quand il est vraiment nécessaire. Mais dans mon cas, il était nécessaire lorsque je configurais l'état de manière programmée. Je suppose qu'il est peut-être appelé automatiquement depuis la classe View dans l'onTouchEvent. Je ferais mieux de l'ajouter dans la méthode setSelected.

1 votes

GiorgioBarchiesi, J'ai deux Button personnalisés, et quand j'essaie de changer le statut des deux boutons à partir de l'événement onClick d'un bouton, seul le bouton cliqué sera modifié, je pense que @buergi a raison que la méthode refreshDrawableState est appelée dans l'événement onClick. Merci encore pour votre merveilleux tutoriel :)

10voto

Ted Hopp Points 122617

Ce fil de discussion montre comment ajouter des états personnalisés aux boutons et similaires. (Si vous ne pouvez pas voir les nouveaux groupes Google dans votre navigateur, il y a une copie du fil de discussion ici.)

0 votes

+1 merci beaucoup, Ted! Pour le moment, l'origine du problème a disparu donc je n'ai pas pu procéder à la mise en œuvre effective. Cependant, si mon client revient sur ce sujet à nouveau, j'essaierai la méthode que tu m'as indiquée.

0 votes

Ressemble exactement à ce dont j'ai besoin, cependant les drawables de la liste d'états pour mes états personnalisés ne changent pas. Je dois passer à côté de quelque chose...

0 votes

Est-ce que vous appelez refreshDrawableState()?

8voto

Nishant Points 348

N'oubliez pas d'appeler refreshDrawableState dans le thread UI :

mHandler.post(new Runnable() {
    @Override
    public void run() {
        refreshDrawableState();
    }
});

Il m'a fallu beaucoup de temps pour comprendre pourquoi mon bouton ne changeait pas son état même si tout semblait correct.

0 votes

Où ou quand devrais-je publier ce gestionnaire?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X