Android: Можно ли использовать string/enum в drawable selector? - программирование

Android: Можно ли использовать string/enum в drawable selector?

Вопросы

Q1: Кто-нибудь смог получить настраиваемый атрибут string/enum, работающий в селекторах xml? Я получил логический атрибут, работающий с помощью [1], но не строкового атрибута.

EDIT: Спасибо за ответы. В настоящее время андроид поддерживает только логические селекторы. См. Принятый ответ по этой причине.

Я планирую реализовать небольшую сложную пользовательскую кнопку, внешний вид которой зависит от двух переменных. Другим будет логический атрибут (true или false) и другой атрибут категории (имеет много разных возможных значений). Мой план состоит в том, чтобы использовать атрибуты boolean и string (или, возможно, enum?). Я надеялся, что могу определить UI в селекторе xml с использованием логического и строкового атрибутов.

Q2. Почему в [1] onCreateDrawableState() логические атрибуты объединяются, только если они истинны?

Это то, что я тестировал, boolean атрибут работает, строка не

ПРИМЕЧАНИЕ. Это просто тестовое приложение, чтобы выяснить, возможен ли атрибут string/enum в селекторе xml. Я знаю, что я могу установить цвет текста кнопки без пользовательского атрибута.

В моем демонстрационном приложении я использую логический атрибут для установки фона кнопки для темного/яркого и строкового атрибута для установки цвета текста, одного из "красного", "зеленого", "синего" }. Атрибуты определены в /res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomButton">
        <attr name="make_dark_background" format="boolean" />
        <attr name="str_attr" format="string" />
    </declare-styleable>
</resources>

Вот селектор, который я хочу достичь:

@drawable/custom_button_background (который работает)

<?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.example.customstringattribute">

    <item app:make_dark_background="true" android:drawable="@color/dark" />
    <item android:drawable="@color/bright" />

</selector>

@color/custom_button_text_color (который не работает)

<selector 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.example.customstringattribute">

    <item app:str_attr="red" android:color="@color/red" />
    <item app:str_attr="green" android:color="@color/green" />
    <item app:str_attr="blue" android:color="@color/blue" />

    <item android:color="@color/grey" />

</selector>

Вот как настраиваемый фон кнопки подключается к логическому селектору, а цвет текста связан с селектором строк.

<com.example.customstringattribute.MyCustomButton
    ...
    android:background="@drawable/custom_button_background"
    android:textColor="@color/custom_button_text_color"
    ...
/>

Вот как загружаются атрибуты в методе init():

private void init(AttributeSet attrs) {

    TypedArray a = getContext().obtainStyledAttributes(attrs,
            R.styleable.MyCustomButton);

        final int N = a.getIndexCount();
        for (int i = 0; i < N; ++i)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
                case R.styleable.MyCustomButton_str_attr:
                    mStrAttr = a.getString(attr);
                    break;
                case R.styleable.MyCustomButton_make_dark_background:
                    mMakeDarkBg  = a.getBoolean(attr, false);
                    break;
            }
        }
        a.recycle();
}

У меня есть массивы int [] для атрибутов

private static final int[] MAKE_DARK_BG_SET = { R.attr.make_dark_background };
private static final int[] STR_ATTR_ID = { R.attr.str_attr };

И те массивы int [] объединяются в допустимое состояние

@Override
protected int[] onCreateDrawableState(int extraSpace) {
    Log.i(TAG, "onCreateDrawableState()");
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
    if(mMakeDarkBg){
        mergeDrawableStates(drawableState, MAKE_DARK_BG_SET);
    }
    mergeDrawableStates(drawableState, STR_ATTR_ID);
    return drawableState;
}

У меня также есть refreshDrawableState() в моих методах настройки атрибутов:

public void setMakeDarkBg(boolean makeDarkBg) {
    if(mMakeDarkBg != makeDarkBg){
        mMakeDarkBg = makeDarkBg;
        refreshDrawableState();
    }
}

public void setStrAttr(String str) {
    if(mStrAttr != str){
        mStrAttr = str;
        refreshDrawableState();
    }
}

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

4b9b3361

Ответ 1

Q1:

Когда вы открываете исходный код StateListDrawable.java, вы можете увидеть этот фрагмент кода в методе inflate, который читает выпадающий селектор xml: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/graphics/java/android/graphics/drawable/StateListDrawable.java

        ...

        for (i = 0; i < numAttrs; i++) {
            final int stateResId = attrs.getAttributeNameResource(i);
            if (stateResId == 0) break;
            if (stateResId == com.android.internal.R.attr.drawable) {
                drawableRes = attrs.getAttributeResourceValue(i, 0);
            } else {
                states[j++] = attrs.getAttributeBooleanValue(i, false)
                        ? stateResId
                        : -stateResId;
            }
        }
        ...

attrs являются атрибутами каждого элемента <item> в <selector>.

В этом for-loop он получает android:drawable, различные атрибуты android:state_xxxx и custom app:xxxx. Все атрибуты android:drawable кажутся интерпретированными только как логические: attrs.getAttributeBooleanValue(....) вызывается.

Я думаю, что это ответ, основанный на исходном коде:

В xml можно добавить только специальные логические атрибуты, а не любой другой тип (включая перечисления).

Q2:

Я не уверен, почему состояние сливается только в том случае, если оно специально установлено в true. Я подозреваю, что код должен был выглядеть следующим образом:

private static final int[] MAKE_DARK_BG_SET     = {  R.attr.make_dark_background };
private static final int[] NOT_MAKE_DARK_BG_SET = { -R.attr.make_dark_background };
....
....
@Override
protected int[] onCreateDrawableState(int extraSpace) {
    Log.i(TAG, "onCreateDrawableState()");
    final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
    mergeDrawableStates(drawableState, mMakeDarkBg? MAKE_DARK_BG_SET : NOT_MAKE_DARK_BG_SET);
    //mergeDrawableStates(drawableState, STR_ATTR_ID);
    return drawableState;
}

Ответ 2

1:

Я не пробовал это сам, но:

Попробовали ли вы разместить @color/custom_button_text_color.xml в папке drawable? (Чтобы быть уверенным, там немного и волшебство папок здесь и там в Android, и я не уверен в этом.)

2:

Существует два варианта использования для состояний. Один из них заключается в том, чтобы явно объявлять селекторами для программных сокращений состояния. В этом случае, для селекторов, вы должны быть в состоянии сказать Android, чтобы использовать эту возможность, если атрибут не установлен. Чтобы выразить это, вы можете включить отрицательные критерии (которым предшествует знак минуса) в int[].

Хотя это нигде не упоминается нигде в контексте критериев выбора, он никогда не упоминается для самопроизвольных состояний (иначе это представление допустимого состояния). Таким образом, один из них определенно находится на безопасной стороне, если в набор не включены отрицаемые идентификаторы состояния; предоставляемые версии Android также не включают их.