Zamień obrazy selektorów programowo

116

Mam ImageView, który ma ustawiony zasób obrazu do rysowania na selektor. Jak programowo uzyskać dostęp do selektora i zmienić obrazy stanu podświetlonego i niewyświetlonego?

Oto kod selektora:

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

Chcę być w stanie zastąpić btn_icon_hli btn_iconinnych obrazów.

dropsOfJupiter
źródło
czy nie byłoby łatwiej mieć dwa selektory i je zamienić?
bigstones
2
Problem polega na tym, że możesz skończyć z setkami plików xml.
Emile,

Odpowiedzi:

244

O ile udało mi się znaleźć (próbowałem zrobić coś podobnego samodzielnie), nie ma możliwości zmodyfikowania pojedynczego stanu po zdefiniowaniu StateListDrawable. Możesz jednak zdefiniować NOWY za pomocą kodu:

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

Możesz po prostu mieć dwa z nich pod ręką lub stworzyć inny, jeśli potrzebujesz.

Kevin Coppock
źródło
1
Nie udało mi się dodać tego do widoku obrazu. setState nie jest na nim dostępny.
dropsOfJupiter
2
właściwie to znalazłem, jego setImageDrawable () Dziękuję bardzo, zadziałało i zaoszczędziło mi około 40 plików xml!
dropsOfJupiter
2
Mam więc inną związaną z tym uwagę. Miałem nadzieję, że możesz odpowiedzieć. Mam ten selektor ustawiony w ImageView, który znajduje się w kontrolce niestandardowej. Kontrolka niestandardowa ma również selektor jako tło. Tak więc selektor całej kontrolki działa, podczas gdy selektor ImageView nie. Czy jest coś, co robię źle? Czy jest jakaś sekwencja?
dropsOfJupiter
1
Nie ma za co! Tak, nie wiem, dlaczego wstawiłem setState, powinno być setImageDrawable, masz rację. Jeśli chodzi o Twoje drugie pytanie, sugerowałbym opublikowanie nowego pytania z kodem niestandardowej kontrolki i jej selektorem, nie jestem pewien odpowiedzi na to pytanie.
Kevin Coppock
3
używam tego samego kodu. zawsze pozostaje obraz, który podałem w ----> nowy stan int [] {}. gdzie się myliłem?
KK_07k11A0585
6

Miałem ten sam problem i poszedłem o krok dalej, aby go rozwiązać. Jedynym problemem jest jednak to, że nie możesz określić NavStateListDrawable w xml, więc musisz ustawić tło elementu interfejsu użytkownika za pomocą kodu. Następnie należy nadpisać metodę onStateChange, aby zapewnić, że za każdym razem, gdy zostanie zmieniony poziom głównego rysunku, zostanie również zaktualizowany poziom listy poziomów podrzędnych.

Tworząc NavStateListDrawable, musisz przejść na poziom ikony, którą chcesz wyświetlić.

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

Dla wszystkich różnych stanów do rysowania przycisku nawigacyjnego mam coś podobnego do następującego.

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

Zamierzałem opublikować to jako pytanie i odpowiedź, ale skoro zadałeś samo pytanie, proszę bardzo. Zwróć uwagę, że oszczędza to mnóstwo definicji plików xml. spadłem z około 50-100 definicji xml do około 4 !.

Emile
źródło
Kod navstatelistdrawable skutecznie sprawia, że ​​xml selektora jest zbędny.
Emile
Cześć Emilie, Czy istnieje możliwość, że elementy do rysowania jako tło przycisku nie pojawią się po raz pierwszy z jakiegokolwiek powodu. Obecnie pojawia się problem polegający na tym, że muszę dotknąć obszaru przycisku, aby pojawiło się tło, lub wyłączyć i wrócić do działania. (Dzieje się to tylko na ekranie hdpi, ale działa dobrze na moim mdpi). Uważam, że inni też mogą mieć ten problem. Czy Twój kod jest testowany pod kątem wszystkich gęstości ekranu?
ryvianstyron
Cześć, nie, to zostało napisane dość dawno temu i dotyczyło tylko jednego urządzenia w tym czasie. Nie jestem pewien, jaki rodzaj problemu może się pojawić, o ile wiem, że wiele gęstości ekranu i układów nie powinno stanowić problemu.
Emile
Dzięki, nie bardzo wiem, co robiłem źle, ale w końcu otrzymałem następujące informacje: 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
To jest pierwsze miejsce, w którym widzę, że wartości ujemne muszą być używane dla stanów ustawionych na fałsz. Dokumentacja nie jest o tym zbyt jasna. Dzięki za wskazówkę!
eocanha