使用 DrawableCompat class 应用 tintList

Using DrawableCompat class to apply a tintList

决定尝试新的 DrawableCompat class。按照 reliable source 的指示,我打电话给:

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。你能告诉我这是为什么吗?

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 着色 iself 而不是像 Lollipop native 那样的可绘制对象(但前提是背景位于白名单,例如默认的 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);
}

你应该把这个怪物放在实用程序中 class。