棒棒糖(Lollipop)的背景色(backgroundTint)对按钮(Button)没有影响。

87

我在我的Activity中有一个按钮,我希望它具有我的主题的强调颜色。与其像在Lollipop之前必须做的那样制作自己的可拉伸图形,我希望使用新的backgroundTint属性。

<Button
    android:id="@+id/btnAddCode"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:backgroundTint="@color/accent"
    android:text="@string/addressInfo_edit_addCode" />

很不幸,它没有效果,按钮仍然是灰色的。

我尝试了不同的backgroundTintMode值,但没有改变任何东西。

我还尝试在我的Activity中编程实现,但也没有改变任何东西。

addCodeView.findViewById(R.id.btnAddCode).setBackgroundTintList(
     getResources().getColorStateList(R.color.accent));

为什么我的着色被忽略了?

编辑: 只是为了澄清,我确实在一个棒棒糖设备上测试。 其它小部件(例如EditText)会正确且自动地进行着色。


3
这是一个已经被修复,将在未来版本中更新的错误,但接受的解决方案可在API 21+上运行。 - alanv
请查看此更新的答案 - Amit Vaghela
16个回答

118

坏消息

如BoD所说,在Lollipop 5.0(API级别21)中给按钮着色是没有意义的。

好消息

Lollipop 5.1(API级别22)似乎通过更改btn_mtrl_default_shape.xml(以及其他文件)来解决了这个问题:https://android.googlesource.com/platform/frameworks/base/+/6dfa60f33ca6018959ebff1efde82db7d2aed1e3%5E!/#F0

绝佳消息

新的支持库(版本22.1+)为许多组件添加了向后兼容的着色支持,包括AppCompatButton

不幸的是,android:backgroundTint 属性仍然无法使用(也许我做错了什么)——因此,您必须在代码中设置 ColorStateList,使用 setSupportBackgroundTintList()。希望将来能够支持 android:backgroundTint更新:Marcio Granzotto评论说,在 AppCompatButton 上可以使用 app:backgroundTint!请注意,它是 app: 而不是 android:,因为它位于应用程序/库中。

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <AppCompatButton
        android:id="@+id/mybutton"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Testing, testing"
        app:backgroundTint="#ff00ff"/>

</LinearLayout>

如果您让活动继承自AppCompatActivity,则它将自动膨胀一个AppCompatButton而不是普通的Button

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton v = (AppCompatButton) findViewById(R.id.mybutton);
        ColorStateList csl = new ColorStateList(new int[][]{new int[0]}, new int[]{0xffffcc00});
        v.setSupportBackgroundTintList(csl);
    }
}

您当然应该从颜色资源中获取ColorStateList,但我有点懒,所以...请注意要将您的应用程序主题基于其中一个Theme.AppCompat主题,否则兼容视图会非常难过...;)这适用于2.3.7(姜饼MR1)和5.0(棒棒糖“经典”)。

1
刚刚添加了一些更多的信息 - 最重要的是,现在可以使用最新版本(22.1)的支持库来实现按钮着色!该支持库是前几天发布的! - Snild Dolkow
8
您可以在使用AppCompat 22.1.1时使用colorButtonNormal(仅为按钮设置主题),它适用于我的4.4.4和5.1版本。 - hunyadym
2
有没有想法如何为TextView设置相同的东西? - android developer
9
你可以使用ColorStateList.valueOf(0xffffcc00);来代替new ColorStateList(new int[][]{new int[0]}, new int[]{0xffffcc00});,这样更加简洁。请注意,这两个语句的意思相同。 - Ashkan Sarlak
5
您可以在xml中使用android.support.v7.widget.AppCompatButton,并使用app:backgroundTint进行背景色调整。 - Marcio Granzotto
显示剩余5条评论

30

看起来给水波纹drawable着色是没有意义的(而按钮的默认背景是水波纹drawable)。

事实上,在查看了平台默认按钮drawable后,我找到了“正确”的方法:您必须在主题中定义它:

    <item name="android:colorButtonNormal">@color/accent</item>

(当然,这只适用于21级以上的玩家。)

警告:由于这是在主题中定义的,因此所有按钮(至少是使用该主题的活动中的所有按钮)都将使用给定的颜色。

作为额外奖励,您还可以通过定义以下内容来更改涟漪颜色:

    <item name="android:colorControlHighlight">@color/accent_ripple</item>

colorControlHighlight 会影响到很多其他的小部件。很高兴你已经解决了这个问题。 - natario
5
您可以通过定义一个覆盖主题(例如没有父级主题并且只定义了一个属性),并使用android:theme属性,将其应用于单个视图。 - alanv
1
那么如何为API 22及以上版本进行修复呢?此外,这个“bug”只会在一些API 21的设备上出现(包括Nexus 5、Galaxy S3)。 - Vedant Agarwala

22

我通常使用 PorterDuff 动态地完成它:

mbutton = (Button) findViewById(R.id.mybutton);
mbutton.getBackground().setColorFilter(anycolor, PorterDuff.Mode.MULTIPLY);

您可以在这里查看不同的混合模式,以及这里的好例子。


1
我使用了PorterDuff.Mode.SRC,它完美地工作了。 - francisco_ssb
完美地运作。在我的情况下,我想在API 17上使用着色效果,这对我很有帮助。 - ashishdhiman2007
2
这应该是被接受的答案。确认在API 20上工作。 - hardanger

22

为了解决Android 5.0.x上与tinting相关的问题,我使用类似以下的代码:

public static void setButtonTint(Button button, ColorStateList tint) {
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP && button instanceof AppCompatButton) {
        ((AppCompatButton) button).setSupportBackgroundTintList(tint);
    } else {
        ViewCompat.setBackgroundTintList(button, tint);
    }
}
它仅在API 21中使用支持方法,而在所有其他情况下使用ViewCompat。

@Marco 使用:if (view instanceof TintableBackgroundView) { ((TintableBackgroundView) view).setSupportBackgroundTintList(tint); } else { ViewCompat.setBackgroundTintList(view, tint); } - chessdork
这在早期版本的设备上,不能与 ImageButton 一起使用。 - Etienne Lawlor
当我使用AppCompatButton.setSupportBackgroundTintList()时,会收到一个“lint warning”警告。只有在同一库组(groupId=com.android.support)中才能调用AppCompatButton.setSupportBackgroundTintList。 - starkej2
它对我起作用了,但我必须反转条件。 - Rodrigo Venancio
为什么会出现这个警告:"只能从同一库组(groupId=com.android.support)内部调用"? - Ferran Negre

20
只需使用app:backgroundTint替换android:backgroundTint,色彩效果将在Lollipop以下版本生效。原因是AppCompatActivity使用AppCompatViewInflater自动将Button或TextView更改为AppCompatButton或AppCompatTextView,然后app:backgroundTint生效。

enter image description here

我在我的项目中使用它,它有效。


18

在 API 19 到 API 27 上进行了测试

<?xml version="1.0" encoding="utf-8"?>
  <android.support.v7.widget.AppCompatButton 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/retry"
    android:textColor="@android:color/white"
    app:backgroundTint="@android:color/holo_red_dark" />
产生输出如下 -

在此输入图像描述


将此标记为已接受的答案,因为经过几年的发展,我相信这现在是官方(也是最好)的方法。(小细节:我相信在大多数情况下,您可以直接使用Button而不是AppCompatButton,它仍然可以正常工作)。 - BoD

9
我认为您需要设置android:background,才能使android:backgroundTint起作用。
更准确地说,我的猜测是您无法对Material主题中默认按钮背景进行backgroundTint,因为它被定义为一个RippleDrawable

1
我认为你是正确的。事实上,我找到了实现我想要的功能的“正确”方法(请参见我的答案)。 - BoD

5
只需使用app:backgroundTint而不是android:backgroundTint。

这个立刻就起作用了。我仍然不明白为什么它似乎可以在所有API版本中运行。 - Taslim Oseni

3
类似问题已在Google上报告:https://code.google.com/p/android/issues/detail?id=201873。但是,在Android Support Library修订版23.2.1(2016年3月)发布后,该错误已得到解决。
问题:FloatingActionButton.setBackgroundTintList(@Nullable ColorStateList tint)不再更改背景颜色。
将Support Library更新至Android Support Library to 23.2.1
使用以下内容的design support library(23.2.1)appcompatwidgets适用于早期版本的Material Design
AppCompat(又名ActionBarCompat)最初是针对运行在Gingerbread上的设备的Android 4.0 ActionBar API的后移,为后移实现和框架实现提供了一个公共API层。AppCompat v21提供了与Android 5.0最新的API和功能集。
Android Support Library 22.1
在使用AppCompat时自动着色小部件的能力非常有助于保持应用程序的强大品牌和一致性。这是在充气布局时自动完成的-将Button替换为AppCompatButton,TextView替换为AppCompatTextView等,以确保每个小部件都支持着色。在此版本中,这些着色感知小部件现已公开提供,即使需要子类化其中一个受支持的小部件,也可以保持着色支持。

2
如果我们查看Support Library的源代码,我们会发现它通常会给已知的按钮着色,但如果我们改变按钮的形状(我有一个圆形按钮),在api<=21中着色效果不好。我们还可以看到TintManager成为了公共类(appcompat-v7:23.1.1),因此我们可以从默认按钮形状(在5.0中涂色正常)中获取ColorStateList,用于当前主题(这样我们就不必创建颜色数组)。
    Context c = ...; // activity
    AppCompatButton ab = ...; // your button
    // works ok in 22+:
    if (Build.VERSION.SDK_INT <= 21) {
        // default appcompat button, that is tinted ok with current theme colors "abc_btn_default_mtrl_shape":
        // ColorStateList tint = TintManager.get(c).getTintList(R.drawable.abc_btn_default_mtrl_shape);
        // Appcompat 23.2 change:
        ColorStateList tint = AppCompatDrawableManager.get().getTintList(c, R.drawable.abc_btn_default_mtrl_shape);
        ab.setSupportBackgroundTintList(tint);
        }

自从AppCompat 23.2版本以后,他们改变了这个类(尽管它是公共的!)TintManager -> AppCompatDrawableManager代码几乎保持不变。 - Pavel Biryukov
WTF... getTintList 从 public 变成 protected 了 ;( 现在需要使用反射 :) - Pavel Biryukov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接