Как добавить пользовательское состояние кнопки - PullRequest
119 голосов
/ 02 декабря 2010

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

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

248 голосов
/ 27 апреля 2011

Решение, обозначенное @ (Тед Хопп), работает, но нуждается в небольшой коррекции: в селекторе состояния элемента нуждаются в префиксе «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): включено, android: выбрано) с пользовательскими состояниями для представления более сложных комбинаций состояний

9 голосов
/ 21 декабря 2010

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

5 голосов
/ 25 июля 2014

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

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

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

...