web-dev-qa-db-fra.com

Comment ajouter un état de bouton personnalisé

Par exemple, le bouton par défaut présente les dépendances suivantes entre ses états et ses images d’arrière-plan:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item Android:state_window_focused="false" Android:state_enabled="true"
        Android:drawable="@drawable/btn_default_normal" />
    <item Android:state_window_focused="false" Android:state_enabled="false"
        Android:drawable="@drawable/btn_default_normal_disable" />
    <item Android:state_pressed="true" 
        Android:drawable="@drawable/btn_default_pressed" />
    <item Android:state_focused="true" Android:state_enabled="true"
        Android:drawable="@drawable/btn_default_selected" />
    <item Android:state_enabled="true"
        Android:drawable="@drawable/btn_default_normal" />
    <item Android:state_focused="true"
        Android:drawable="@drawable/btn_default_normal_disable_focused" />
    <item
        Android:drawable="@drawable/btn_default_normal_disable" />
</selector>

Comment puis-je définir mon propre état personnalisé (smth like Android:state_custom), alors je pourrais l’utiliser pour changer dynamiquement l’aspect visuel de mon bouton?

121
Vit Khudenko

La solution indiquée par @ (Ted Hopp) fonctionne, mais nécessite une petite correction: dans le sélecteur, les états d'item nécessitent un préfixe "app:", sinon l'inflateur ne reconnaîtra pas correctement l'espace de nom et échouera en silence; au moins c'est ce qui m'arrive.

Permettez-moi de vous rapporter ici la solution complète, avec quelques détails supplémentaires:

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

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="food">
        <attr name="state_fried" format="boolean" />
        <attr name="state_baked" format="boolean" />
    </declare-styleable>
</resources>

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

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

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

En outre, 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;}

Puis 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 casse-tête; le sélecteur définissant StateListDrawable que vous utiliserez comme arrière-plan pour votre widget. C'est le fichier "res/drawable/food_button.xml":

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res/com.mydomain.mypackage">
<item
    app:state_baked="true"
    app:state_fried="false"
    Android:drawable="@drawable/item_baked" />
<item
    app:state_baked="false"
    app:state_fried="true"
    Android:drawable="@drawable/item_fried" />
<item
    app:state_baked="true"
    app:state_fried="true"
    Android:drawable="@drawable/item_overcooked" />
<item
    app:state_baked="false"
    app:state_fried="false"
    Android:drawable="@drawable/item_raw" />
</selector>

Notez le préfixe "app:" alors qu'avec le standard Android indique que vous auriez utilisé le préfixe "Android:". L'espace de nommage 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 actuel 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 utilisez Tools R17 ou une version ultérieure) 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 XML de mise en page, vous devez spécifier le nom complet (par exemple, com.mydomain.mypackage.FoodButton).

  • Vous pouvez également mélanger des états standard (par exemple, Android: pressé, Android: activé, Android: sélectionné) avec des états personnalisés, afin de représenter des combinaisons d’états plus complexes.

252
Giorgio Barchiesi

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

10
Ted Hopp

N'oubliez pas d'appeler refreshDrawableState dans le fil de l'interface utilisateur:

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

J'ai mis beaucoup de temps à comprendre pourquoi mon bouton ne change pas d'état alors que tout semble aller pour le mieux.

5
Nishant Soni