Як додати спеціальний стан кнопки


132

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

<?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), щоб потім міг використовувати його для динамічної зміни візуального вигляду кнопки?


Я хотів отримати додаткові стани для перегляду EditText, щоб визначити, коли два поля паролів збігаються, щоб відобразити невелику галочку.
Натан Шверман

Відповіді:


275

Рішення, вказане @ (Тед Хопп), працює, але потребує невеликої корекції: у селекторі для пункту пункту потрібен префікс "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", похідний від класу "Кнопка". Вам доведеться реалізувати конструктор; реалізуйте цей, який, здається, той, який використовує надувальник:

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

Тоді перезазначте функцію "onCreateDravableState":

@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 / dravable / 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:". Простір імен XML має вирішальне значення для правильної інтерпретації інфлятора і залежить від типу проекту, в який ви додаєте атрибути. Якщо це програма, замініть com.mydomain.mypackage фактичною назвою пакета вашої програми (назва програми виключена). Якщо це бібліотека, ви повинні використовувати "http://schemas.android.com/apk/res-auto" (і використовувати Інструменти R17 або новіші версії), або ви отримаєте помилки під час виконання.

Пара приміток:

  • Здається, вам не потрібно викликати функцію "refreshDravableState", принаймні рішення добре працює, як і в моєму випадку

  • Щоб використовувати свій власний клас у файлі xml-макета, вам доведеться вказати повністю кваліфіковане ім’я (наприклад, com.mydomain.mypackage.FoodButton)

  • Ви можете використовувати стандартні стани (наприклад, android: натиснутий, android: увімкнено, android: вибрано) за допомогою спеціальних станів, щоб представити більш складні комбінації станів


3
Оновлення: якщо користувацький клас походить від TextView, а не кнопки, виклик refreshDravableState видається необхідним, інакше зовнішній вигляд віджета не оновлюється. Виклик повинен бути розміщений у програмах. Я не пробував інших занять. Випробування виконували на пристрої froyo.
Джорджіо Барчізі

17
Це refreshDrawableState, безумовно, важливо. Я не зовсім впевнений, коли це дійсно потрібно. Але в моєму випадку це було потрібно при встановленні стану програмно. Я думаю, що він, можливо, викликається з класу View автоматично в onTouchEvent. Я б краще додати його в метод setSelected.
buergi

1
GiorgioBarchiesi, у мене є дві спеціальні кнопки, і коли я намагаюся змінити стан обох кнопок із події onClick однієї кнопки, зміниться лише натиснута кнопка, я думаю, що @buergi має рацію, що метод refreshDravableState викликається в onClickEvent. Ще раз дякую за ваш чудовий підручник :)
Болтон

2
Але як можна використовувати спеціальні стани, яких немає boolean? Або селектори працюють лише на булевих?
Peterdk

2
Як це працює? Я маю на увазі, як атрибут оновлюється до стану true / false? Хто його оновлює? Чи об'єднує Dravablestate, лише якщо локальна змінна є правдою, оновлює стан або значення атрибута? Який код точно буде оновлений R.attr.state_fried?
kAmol

10

Цей потік показує, як додати користувацькі стани до кнопок тощо. (Якщо ви не можете побачити нові групи Google в вашому браузері, є копія нитки тут .)


+1 велике спасибі, Теде! Зараз походження проблеми пішло, тому я не дійшов до реальної реалізації. Однак якщо мій клієнт повернеться до цього ще раз, я спробую так, як ти мені вказав.
Віт Худенко

Виглядає як те, що мені потрібно, проте малюнки в списку держав для моїх спеціальних станів не змінюються, я повинен щось пропустити ...
Натан Шверман

Ви викликаєте refreshDravableState ()?
Тед Хопп

Посилання мертві.
Мітч

@Mitch - Ну, це дуже погано. Я побачу, чи можу я знайти деякі посилання на заміну. Якщо ні, я видалю цю відповідь, оскільки це в принципі марно. Тим часом у прийнятій відповіді є вся необхідна інформація.
Тед

6

Будь ласка, не забудьте зателефонувати refreshDrawableStateв межах потоку інтерфейсу:

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

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


де чи коли я повинен розмістити цей обробник?
Артур Мело
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.