Использование класса DrawableCompat для применения tintList

Решил попробовать новый DrawableCompat учебный класс. Следуя инструкциям из надежного источника, я звоню:

Button b = (Button) findViewById(R.id.button);
Drawable d = b.getBackground();
d = DrawableCompat.wrap(d);
DrawableCompat.setTintList(d, getResources().getColorStateList(...));

Удивительно, но это не работает: фон моей кнопки получает цвет, который я определяю для не нажатого, не сфокусированного состояния, но он не меняется при нажатии / на фокусе.

Я смог добиться успеха совершенно по-другому,

Button b = (Button) findViewById(R.id.button);
AppCompatButton b2 = (AppCompatButton) b; //direct casting to AppCompatButton throws annoying warning
b2.setSupportBackgroundTintList(getResources().getColorStateList(...));

который работает и еще более компактен, но я хотел использовать DrawableCompat вместо. Не могли бы вы сказать мне, почему это?

1 ответ

Решение

d = DrawableCompat.wrap(d); создает новый экземпляр, если он еще не DrawableWrapper поэтому вы подкрашиваете этот новый экземпляр, но оригинал, который хранится в кнопке, остается прежним.

Весь код будет выглядеть примерно так

Button b = (Button) findViewById(R.id.button);
Drawable d = b.getBackground();
d = DrawableCompat.wrap(d);
DrawableCompat.setTintList(d, getResources().getColorStateList(...));
b.setBackground(d); // or setBackgroundDrawable on older platforms

Так что да, я бы выбрал второй подход, который вы описали, потому что он отвлекает вас от тяжелой работы.

РЕДАКТИРОВАТЬ:

Просто погрузился в код appcompat и обнаружил, что AppCompatButton в отличие от нативного Lollipop, окрашивает себя, а не рисуемое (но только если фон находится в белом списке, например, кнопка appcompat по умолчанию рисуется). Таким образом, вы должны очистить оттенок от самой кнопки в первую очередь.

Button b = (Button) findViewById(R.id.button);

if (b instanceof AppCompatButton) {
    ((AppCompatButton)b).setSupportBackgroundTintList(null);
}

Drawable d = b.getBackground();
d = DrawableCompat.wrap(d);
DrawableCompat.setTintList(d, getResources().getColorStateList(...));
b.setBackground(d); // or setBackgroundDrawable on older platforms

РЕДАКТИРОВАТЬ 2:

Приведенный выше код будет NullPointerException при попытке сбросить список оттенков кнопки. В настоящее время я подаю отчет об ошибке.

В то же время, я предлагаю вам надуть кнопку с помощью собственного фона (не белого для тонирования appcompat) напрямую или с помощью @null фон и разрешение фона кнопки по умолчанию

TypedArray ta = context.obtainStyledAttributes(null, new int[]{android.R.attr.background}, R.attr.buttonStyle, R.style.Widget_AppCompat_Button);
Drawable d = ta.getDrawable(0);
ta.recycle();

Окончательное решение

Так как все это выглядит довольно неопрятно, самое простое (и единственное в своем роде рабочее и надежное решение, но в то же время нелепое) для вас сейчас это:

Button b = (Button) findViewById(R.id.button);
ColorStateList c = getResources().getColorStateList(...);
Drawable d = b.getBackground();
if (b instanceof AppCompatButton) {
    // appcompat button replaces tint of its drawable background
    ((AppCompatButton)b).setSupportBackgroundTintList(c);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Lollipop button replaces tint of its drawable background
    // however it is not equal to d.setTintList(c)
    b.setBackgroundTintList(c);
} else {
    // this should only happen if 
    // * manually creating a Button instead of AppCompatButton
    // * LayoutInflater did not translate a Button to AppCompatButton
    d = DrawableCompat.wrap(d);
    DrawableCompat.setTintList(d, c);
    b.setBackgroundDrawable(d);
}

Вы должны убрать это чудовище в служебный класс.

Другие вопросы по тегам