Reemplazar las imágenes del selector mediante programación

116

Tengo un ImageView que tiene un conjunto de recursos de imagen dibujable en un selector. ¿Cómo accedo programáticamente al selector y cambio las imágenes del estado resaltado y no resaltado?

Aquí hay un código de selector:

<?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>

Quiero poder reemplazar btn_icon_hly btn_iconcon otras imágenes.

gotas de Jupiter
fuente
¿No sería más fácil tener dos selectores e intercambiarlos?
bigstones
2
El problema con eso es que puede terminar con cientos de archivos xml.
Emile

Respuestas:

244

Por lo que he podido encontrar (he intentado hacer algo similar yo mismo), no hay forma de modificar un solo estado después de que StateListDrawable ya se haya definido. Sin embargo, puede definir uno NUEVO a través del código:

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

Y puede tener dos de ellos a mano o crear uno diferente según lo necesite.

Kevin Coppock
fuente
1
No pude agregar esto a una vista de imagen. setState no está disponible en él.
dropsOfJupiter
2
de hecho lo encontré, es setImageDrawable () ¡Muchas gracias, funcionó y me salvó unos 40 archivos xml!
dropsOfJupiter
2
Entonces tengo otra nota relacionada con esto. Esperaba que pudieras responder. Tengo este selector configurado en ImageView que está dentro del Control personalizado. El control personalizado también tiene un selector como fondo. Entonces, el selector de todo el control funciona, mientras que el selector ImageView no. ¿Hay algo que esté haciendo mal? ¿Existe una secuencia?
dropsOfJupiter
1
¡De nada! Sí, no sé por qué puse setState, debería ser setImageDrawable, tienes razón. Según su otra pregunta, le sugiero que publique una nueva pregunta con el código para su control personalizado y su selector, no estoy seguro de la respuesta a eso.
Kevin Coppock
3
estoy usando el mismo código. siempre permanece la imagen que he especificado en ----> new int [] {} state. donde me equivoqué?
KK_07k11A0585
6

Tuve el mismo problema y di un paso más para solucionarlo. Sin embargo, el único problema es que no puede especificar NavStateListDrawable en xml, por lo que debe establecer el fondo de su elemento de interfaz de usuario a través del código. El método onStateChange debe anularse para asegurarse de que cada vez que se cambia el nivel del elemento de dibujo principal, también se actualiza el nivel de la lista de niveles secundarios.

Al construir NavStateListDrawable, debe pasar el nivel del icono que desea mostrar.

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

Para todos los diferentes estados de diseño del botón de navegación, tengo algo como lo siguiente.

<?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>

Iba a publicar esto como una pregunta y una respuesta, pero como has hecho la misma pregunta, aquí tienes. Tenga en cuenta que esto le ahorra una gran cantidad de definiciones de archivos xml. Bajé de aproximadamente 50-100 definiciones xml a aproximadamente 4.

Emile
fuente
El código navstatelistdrawable efectivamente hace que el selector xml sea redundante.
Emile
Hola emilie, ¿Existe la posibilidad de que los elementos de diseño como fondo de un botón no se muestren la primera vez por algún motivo? Actualmente tengo el problema en el que necesito tocar el área del botón para que aparezca el fondo, o cambiar y volver a la actividad. (Esto solo ocurre en una pantalla hdpi, pero funciona bien en mi mdpi). Creo que otros también pueden tener este problema. ¿Su código está probado para todas las densidades de pantalla?
Ryvianstyron
Hola, no, esto fue escrito hace un tiempo y era solo para un dispositivo en ese momento. Sin embargo, no estoy seguro de qué tipo de problema podría surgir, hasta donde yo sé, las densidades y diseños de pantalla múltiples no deberían representar un problema.
Emile
Gracias, no sé muy bien qué estaba haciendo mal, pero al final solo tuve lo siguiente: buttonStates = new StateListDrawable (); buttonStates.addState (nuevo int [] {statePressed}, ApplicationConstants.moduleImageLoader.findImageByName (drawable_pressed)); buttonStates.addState (new int [] {- ​​stateFocused, -statePressed, -stateSelected}, ApplicationConstants.moduleImageLoader.findImageByName (drawable_normal));
ryvianstyron
1
Este es el primer lugar donde veo que los valores negativos deben usarse para los estados establecidos en falso. La documentación no es muy clara al respecto. ¡Gracias por el consejo!
eocanha