Как добавить пользовательское состояние кнопки

Например, кнопка по умолчанию имеет следующие зависимости между состояниями и фоновыми изображениями:

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

Как я могу определить собственное пользовательское состояние ( android:state_custom , вроде android:state_custom ), поэтому я мог бы использовать его для динамического изменения внешнего вида кнопки?

Решение, указанное @ (Ted Hopp), работает, но нуждается в небольшой коррекции: в селекторе элемент состояния должен иметь префикс «app:», иначе надуватель не будет правильно распознавать пространство имен и будет терпеть неудачу; По крайней мере, это то, что происходит со мной.

Позвольте мне сообщить здесь все решение, с некоторыми подробностями:

Сначала создайте файл «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> 

Затем определите свой собственный класс. Например, это может быть класс «FoodButton», полученный из класса «Button». Вам нужно будет реализовать конструктор; Реализовать этот, который, по-видимому, используется надувом:

 public FoodButton(Context context, AttributeSet attrs) { super(context, attrs); } 

Наверх производного класса:

 private static final int[] STATE_FRIED = {R.attr.state_fried}; private static final int[] STATE_BAKED = {R.attr.state_baked}; 

Кроме того, переменные состояния:

 private boolean mIsFried = false; private boolean mIsBaked = false; 

И пара сеттеров:

 public void setFried(boolean isFried) {mIsFried = isFried;} public void setBaked(boolean isBaked) {mIsBaked = isBaked;} 

Затем переопределите функцию «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; } 

Наконец, самая тонкая часть этой головоломки; Селектор, определяющий StateListDrawable, который вы будете использовать в качестве фона для вашего виджета. Это файл «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> 

Обратите внимание на префикс «app:», тогда как со стандартными состояниями Android вы использовали бы префикс «android:». Пространство имен XML имеет решающее значение для правильной интерпретации надувом и зависит от типа проекта, в котором вы добавляете атрибуты. Если это приложение, замените com.mydomain.mypackage на фактическое имя пакета вашего приложения (исключение имени приложения). Если это библиотека, вы должны использовать «http://schemas.android.com/apk/res-auto&#xBB; (и использовать инструменты R17 или новее), или вы получите ошибки времени выполнения.

Несколько примечаний:

  • Кажется, вам не нужно вызывать функцию refreshDrawableState, по крайней мере, решение работает хорошо, как в моем случае

  • Чтобы использовать свой собственный класс в XML-файле макета, вам нужно будет указать полное имя (например, com.mydomain.mypackage.FoodButton)

  • Вы можете как стандартное состояние смешивания смешивать (например, андроид: нажатый, андроид: включен, андроид: выбран) с пользовательскими состояниями, чтобы представлять более сложные комбинации состояний

В этом потоке показано, как добавлять пользовательские состояния к кнопкам и тому подобное. (Если вы не видите новые группы Google в своем браузере, здесь есть копия потока.)

Не забудьте вызвать refreshDrawableState в потоке пользовательского интерфейса:

 mHandler.post(new Runnable() { @Override public void run() { refreshDrawableState(); } }); 

Мне потребовалось много времени, чтобы понять, почему моя кнопка не меняет свое состояние, хотя все выглядит правильно.