Na przykład przycisk domyślny ma następujące zależności między swoimi stanami a obrazami tła:
<?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>
Jak mogę zdefiniować własny stan niestandardowy (coś podobnego android:state_custom
), aby móc go użyć do dynamicznej zmiany wyglądu przycisku?
Odpowiedzi:
Rozwiązanie wskazane przez @ (Ted Hopp) działa, ale wymaga niewielkiej korekty: w selektorze stany pozycji wymagają prefiksu „app:”, w przeciwnym razie inflater nie rozpozna poprawnie przestrzeni nazw i po cichu zawiedzie; przynajmniej tak się ze mną dzieje.
Pozwólcie, że opiszę tutaj całe rozwiązanie, z kilkoma szczegółami:
Najpierw utwórz plik „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>
Następnie zdefiniuj swoją klasę niestandardową. Na przykład może to być klasa „FoodButton” pochodząca z klasy „Button”. Będziesz musiał zaimplementować konstruktora; wdrożyć ten, który wydaje się być tym używanym przez inflatera:
public FoodButton(Context context, AttributeSet attrs) { super(context, attrs); }
Oprócz klasy pochodnej:
private static final int[] STATE_FRIED = {R.attr.state_fried}; private static final int[] STATE_BAKED = {R.attr.state_baked};
Ponadto zmienne stanu:
private boolean mIsFried = false; private boolean mIsBaked = false;
I kilka seterów:
public void setFried(boolean isFried) {mIsFried = isFried;} public void setBaked(boolean isBaked) {mIsBaked = isBaked;}
Następnie zastąp funkcję „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; }
Wreszcie najdelikatniejszy element tej układanki; selektor definiujący StateListDrawable, którego będziesz używać jako tła dla widgetu. To jest plik „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>
Zwróć uwagę na prefiks „app:”, podczas gdy w standardowych stanach Androida użyłbyś prefiksu „android:”. Przestrzeń nazw XML jest kluczowa dla poprawnej interpretacji przez inflater i zależy od typu projektu, w którym dodajesz atrybuty. Jeśli jest to aplikacja, zamień com.mydomain.mypackage na rzeczywistą nazwę pakietu aplikacji (bez nazwy aplikacji). Jeśli jest to biblioteka, musisz użyć „http://schemas.android.com/apk/res-auto” (i używać narzędzi w wersji 17 lub nowszej), w przeciwnym razie wystąpią błędy w czasie wykonywania.
Kilka uwag:
Wygląda na to, że nie musisz wywoływać funkcji "refreshDrawableState", przynajmniej rozwiązanie działa dobrze tak jak w moim przypadku
Aby użyć własnej klasy w pliku xml układu, musisz określić w pełni kwalifikowaną nazwę (np. Com.mydomain.mypackage.FoodButton)
Możesz dowolnie mieszać stany standardowe (np. Android: wciśnięty, android: włączony, android: wybrany) ze stanami niestandardowymi, aby przedstawić bardziej skomplikowane kombinacje stanów
źródło
refreshDrawableState
jest zdecydowanie ważne. Nie jestem absolutnie pewien, kiedy jest to naprawdę potrzebne. Ale w moim przypadku było to potrzebne przy programowym ustawianiu stanu. Myślę, że jest to prawdopodobnie wywoływane automatycznie z klasy View w zdarzeniu onTouchEvent. Lepiej dodam to w metodzie setSelected.boolean
? A może selektory działają tylko na logicznych?W tym wątku pokazano, jak dodawać niestandardowe stany do przycisków i tym podobnych. (Jeśli nie widać nowych grup Google w swojej przeglądarce, nie kopię wątku tutaj .)
źródło
Nie zapomnij zadzwonić
refreshDrawableState
w wątku UI:mHandler.post(new Runnable() { @Override public void run() { refreshDrawableState(); } });
Zajęło mi dużo czasu, aby dowiedzieć się, dlaczego mój przycisk nie zmienia swojego stanu, mimo że wszystko wygląda dobrze.
źródło