Substitua as imagens do seletor de maneira programática

116

Eu tenho um ImageView que tem um recurso de imagem drawable definido como um seletor. Como faço para acessar programaticamente o seletor e alterar as imagens do estado realçado e não realçado?

Aqui está um código de seletor:

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

Eu quero ser capaz de substituir btn_icon_hle btn_iconcom outras imagens.

quedas de Jupiter
fonte
não seria mais fácil ter dois seletores e trocá-los?
bigstones de
2
O problema com isso é que você pode acabar com centenas de arquivos xml.
Emile

Respostas:

244

Até onde pude descobrir (eu tentei fazer algo semelhante), não há como modificar um único estado depois que o StateListDrawable já foi definido. Você pode, no entanto, definir um NOVO por meio do 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);

E você pode apenas manter dois deles em mãos ou criar um diferente conforme necessário.

Kevin Coppock
fonte
1
Não consegui adicionar isso a uma visualização de imagem. setState não está disponível nele.
dropsOfJupiter de
2
na verdade eu encontrei, é setImageDrawable () Muito obrigado, funcionou e me salvou cerca de 40 arquivos xml!
dropsOfJupiter de
2
Portanto, tenho outra nota relacionada a isso. Eu esperava que você pudesse responder. Eu tenho esse seletor definido no ImageView que está dentro do controle personalizado. O controle personalizado também tem um seletor como plano de fundo. Portanto, o seletor de todo o controle funciona, enquanto o seletor ImageView não. Há algo que estou fazendo de errado? Existe uma sequência?
dropsOfJupiter de
1
De nada! Sim, eu não sei porque coloquei setState, deveria ser setImageDrawable, você está certo. De acordo com sua outra pergunta, sugiro postar uma nova pergunta com o código do seu controle personalizado e seu seletor, não tenho certeza da resposta para isso.
Kevin Coppock
3
estou usando o mesmo código. sempre a imagem que especifiquei em ----> new int [] {} state permanece. onde eu estava errado ??
KK_07k11A0585
6

Eu tive o mesmo problema e dei um passo adiante para resolvê-lo. O único problema, entretanto, é que você não pode especificar o NavStateListDrawable em xml, então você deve definir o plano de fundo do seu elemento de IU por meio do código. O método onStateChange deve então ser substituído para garantir que toda vez que o nível do drawable principal for alterado, você também atualize o nível da lista de níveis filho.

Ao construir o NavStateListDrawable, você deve passar o nível do ícone que deseja exibir.

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 os diferentes estados de drawable do botão de navegação, tenho algo como o seguinte.

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

Eu ia postar isso como uma pergunta e resposta, mas visto que você fez a própria pergunta, aqui está. Observe, isso economiza um monte de definições de arquivo xml. Eu diminuí de cerca de 50-100 definições de xml para cerca de 4 !.

Emile
fonte
O código navstatelistdrawable torna efetivamente o seletor xml redundante.
Emile
Olá, emilie, Existe a possibilidade de que os drawables como plano de fundo do botão não apareçam na primeira vez por algum motivo. Atualmente estou tendo um problema em que preciso tocar na área do botão para que o plano de fundo apareça ou desligar e voltar à atividade. (Isso só acontece em uma tela hdpi, mas funciona bem no meu mdpi). Eu acredito que outros podem ter esse problema também. Seu código foi testado para todas as densidades de tela?
ryvianstyron
Olá, não, isso foi escrito há um bom tempo e era apenas para um dispositivo no momento. Não tenho certeza de que tipo de problema pode surgir, embora, pelo que sei, múltiplas densidades de tela e layouts não devam ser um problema.
Emile
Obrigado, não sei bem o que estava fazendo de errado, mas no final eu só tinha o seguinte: 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
Este é o primeiro lugar onde vejo que valores negativos devem ser usados ​​para estados definidos como falsos. A documentação não é muito clara sobre isso. Obrigado pela dica!
eocanha,