Remplacer les images du sélecteur par programme

116

J'ai un ImageView qui a une ressource d'image dessinable définie sur un sélecteur. Comment puis-je accéder par programme au sélecteur et modifier les images de l'état en surbrillance et non en surbrillance?

Voici un code de sélecteur:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/iconSelector">
  <!-- pressed -->
  <item android:state_pressed="true" android:drawable="@drawable/btn_icon_hl" />
  <!-- focused -->
  <item android:state_focused="true" android:drawable="@drawable/btn_icon_hl" />
  <!-- default -->
  <item android:drawable="@drawable/btn_icon" />
</selector>

Je veux pouvoir remplacer btn_icon_hlet btn_iconavec d'autres images.

les rejets de Jupiter
la source
ne serait-il pas plus facile d'avoir deux sélecteurs et de les échanger?
bigstones
2
Le problème est que vous pouvez vous retrouver avec des centaines de fichiers xml.
Emile

Réponses:

244

Autant que j'ai pu trouver (j'ai essayé de faire quelque chose de similaire moi-même), il n'y a aucun moyen de modifier un seul état après que StateListDrawable a déjà été défini. Vous pouvez cependant en définir un NOUVEAU via le code:

StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.state_pressed},
    getResources().getDrawable(R.drawable.pressed));
states.addState(new int[] {android.R.attr.state_focused},
    getResources().getDrawable(R.drawable.focused));
states.addState(new int[] { },
    getResources().getDrawable(R.drawable.normal));
imageView.setImageDrawable(states);

Et vous pouvez simplement en garder deux sous la main ou en créer un différent selon vos besoins.

Kevin Coppock
la source
1
Je n'ai pas pu ajouter ceci à une vue d'image. setState n'y est pas disponible.
dropsOfJupiter
2
en fait je l'ai trouvé, son setImageDrawable () Merci beaucoup cela a fonctionné et m'a sauvé environ 40 fichiers xml!
dropsOfJupiter
2
J'ai donc une autre note à ce sujet. J'espérais que vous pourrez répondre. J'ai ce sélecteur défini sur ImageView qui se trouve à l'intérieur du contrôle personnalisé. Le contrôle personnalisé a également un sélecteur en arrière-plan. Ainsi, le sélecteur de l'ensemble du contrôle fonctionne, contrairement au sélecteur ImageView. Y a-t-il quelque chose que je fais mal? Y a-t-il une séquence?
dropsOfJupiter
1
Vous êtes les bienvenus! Ouais, je ne sais pas pourquoi j'ai mis setState, devrait être setImageDrawable, vous avez raison. Selon votre autre question, je vous suggère de publier une nouvelle question avec le code de votre contrôle personnalisé et de son sélecteur, je ne suis pas sûr de la réponse à cela.
Kevin Coppock le
3
j'utilise le même code. l'image que j'ai spécifiée dans ----> new int [] {} reste toujours. où je me suis trompé ??
KK_07k11A0585
6

J'ai eu le même problème et je suis allé plus loin pour le résoudre. Le seul problème cependant est que vous ne pouvez pas spécifier le NavStateListDrawable dans xml, vous devez donc définir l'arrière-plan de votre élément d'interface utilisateur via le code. La méthode onStateChange doit ensuite être remplacée pour garantir que chaque fois que le niveau du dessin principal est modifié, vous mettez également à jour le niveau de la liste des niveaux enfants.

Lors de la construction du NavStateListDrawable, vous devez passer le niveau de l'icône que vous souhaitez afficher.

public class NavStateListDrawable extends StateListDrawable {

    private int level;

    public NavStateListDrawable(Context context, int level) {

        this.level = level;
        //int stateChecked = android.R.attr.state_checked;
        int stateFocused = android.R.attr.state_focused;
        int statePressed = android.R.attr.state_pressed;
        int stateSelected = android.R.attr.state_selected;

        addState(new int[]{ stateSelected      }, context.getResources().getDrawable(R.drawable.nav_btn_pressed));
        addState(new int[]{ statePressed      }, context.getResources().getDrawable(R.drawable.nav_btn_selected));
        addState(new int[]{ stateFocused      }, context.getResources().getDrawable(R.drawable.nav_btn_focused));

        addState(new int[]{-stateFocused, -statePressed, -stateSelected}, context.getResources().getDrawable(R.drawable.nav_btn_default));


    }

    @Override
    protected boolean onStateChange(int[] stateSet) {

        boolean nowstate = super.onStateChange(stateSet);

        try{
            LayerDrawable defaultDrawable = (LayerDrawable)this.getCurrent();


            LevelListDrawable bar2 =  (LevelListDrawable)defaultDrawable.findDrawableByLayerId(R.id.nav_icons);
            bar2.setLevel(level);
        }catch(Exception exception)
        {

        }

        return nowstate;
    }
}

Pour tous les différents états dessinables du bouton de navigation, j'ai quelque chose comme ce qui suit.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

   <item android:drawable="@drawable/top_bar_default" >

   </item>

    <item android:id="@+id/nav_icons" android:bottom="0dip">
        <level-list xmlns:android="http://schemas.android.com/apk/res/android">
            <item android:maxLevel="0" >
                <bitmap
                    android:src="@drawable/top_bar_icon_back"
                    android:gravity="center" />
            </item>
            <item android:maxLevel="1" >
                <bitmap
                    android:src="@drawable/top_bar_icon_nav"
                    android:gravity="center" />
            </item>
            <item android:maxLevel="2" >
                <bitmap
                    android:src="@drawable/top_bar_icon_settings"
                    android:gravity="center" />
            </item>
            <item android:maxLevel="3" >
                <bitmap
                    android:src="@drawable/top_bar_icon_search"
                    android:gravity="center" />
            </item>
        </level-list>

    </item>

</layer-list>

J'allais poster ceci sous forme de question et réponse, mais vu que vous avez posé la question même, c'est parti. Notez que cela vous permet d'économiser beaucoup de définitions de fichiers xml. je suis passé d'environ 50-100 définitions xml à environ 4 !.

Emile
la source
Le code navstatelistdrawable rend effectivement le sélecteur xml redondant.
Emile
Salut Emilie, Y a-t-il une possibilité que les drawables comme arrière-plan de bouton n'apparaissent pas la première fois pour une raison quelconque. Actuellement, je reçois le problème où je dois toucher la zone du bouton pour que l'arrière-plan apparaisse, ou basculer et revenir à l'activité. (Cela ne se produit que sur un écran hdpi, mais fonctionne bien sur mon mdpi). Je pense que d'autres peuvent aussi avoir ce problème. Votre code est-il testé pour toutes les densités d'écran?
ryvianstyron
Salut, non, cela a été écrit il y a assez longtemps et n'était que pour un seul appareil à la fois. Je ne suis pas sûr du type de problème qui pourrait survenir, à ma connaissance, plusieurs densités et mises en page d'écran ne devraient pas poser de problème.
Emile
Merci, je ne sais pas trop ce que je faisais de mal, mais à la fin, j'ai juste eu ce qui suit: buttonStates = new StateListDrawable (); buttonStates.addState (new int [] {statePressed}, ApplicationConstants.moduleImageLoader.findImageByName (drawable_pressed)); buttonStates.addState (new int [] {- ​​stateFocused, -statePressed, -stateSelected}, ApplicationConstants.moduleImageLoader.findImageByName (drawable_normal));
ryvianstyron
1
C'est le premier endroit où je vois que des valeurs négatives doivent être utilisées pour les états définis sur false. La documentation n'est pas très claire à ce sujet. Merci pour le conseil!
eocanha