Как добавить пользовательское состояние кнопки
Например, кнопка по умолчанию имеет следующие зависимости между ее состояниями и фоновыми изображениями:
<?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
), тогда я мог бы использовать его для динамического изменения внешнего вида моей кнопки?
3 ответа
Решение, обозначенное @(Тед Хопп), работает, но нуждается в небольшой коррекции: в селекторе состояния элемента нуждаются в префиксе "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" (и использовать Tools R17 или новее), иначе вы получите ошибки во время выполнения.
Пара заметок:
Кажется, вам не нужно вызывать функцию "refreshDrawableState", по крайней мере, решение работает хорошо, как есть, в моем случае
Чтобы использовать пользовательский класс в XML-файле макета, вам необходимо указать полное имя (например, com.mydomain.mypackage.FoodButton).
Вы можете смешивать стандартные состояния (например, android: нажатый, android: enabled, android: selected) с пользовательскими состояниями, чтобы представлять более сложные комбинации состояний
Этот поток показывает, как добавить пользовательские состояния для кнопок и тому подобное. (Если вы не видите новые группы Google в своем браузере, здесь есть копия темы.)
Пожалуйста, не забудьте позвонить refreshDrawableState
в потоке пользовательского интерфейса:
mHandler.post(new Runnable() {
@Override
public void run() {
refreshDrawableState();
}
});
Мне потребовалось много времени, чтобы понять, почему моя кнопка не меняет своего состояния, хотя все выглядит правильно.