API <21的可绘制图标着色问题

85

在 API < 21 的情况下,是否有可能使可绘制的着色工作?

<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_calendar"
    android:tint="@color/primary" />

功能很好,但只限于使用API21的设备。有没有适用于较低API设备或AppCompat支持的解决方法?找不到任何信息。

10个回答

111

使用 AppCompatImageView 的方法如下:

<android.support.v7.widget.AppCompatImageView
        android:id="@+id/my_appcompat_imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/my_image"
        android:tint="#636363"
    />

请确保您的应用的 build.gradle 文件中有最新的 appcompat-v7

例如,在您的应用的 build.gradle 文件中添加 compile 'com.android.support:appcompat-v7:25.0.0'


67
AppCompatImageView文档中得知:使用ImageView在布局中时,这个类将会自动被使用。只有当编写自定义视图时才需要手动使用此类。因此,在布局中使用普通的ImageView应该可以正常工作。 - Nimrod Dayan
1
正如@NimrodDayan上面提到的,这并不是必要的。然而,我收到了在三星A5和Moto G上android:tint无法工作的报告(使用appcompat-v7:23.4.0),因此在某些设备上可能没有正确替换ImageViews。 - Stephen Kidson
@StephenKidson,我正在使用相同版本的appcompat,并在某个未知品牌设备上遇到了相同的问题。你解决了吗?我想知道是否有关于这个问题的bug报告... - Nimrod Dayan
@NimrodDayan通过为我要着色的颜色制作单独的可绘制XML来解决了这个问题 - 这不是理想的解决方法,但几乎是确保问题得到解决而无需获取每个受影响设备的唯一方法。 - Stephen Kidson
4
在使用appcompat-v7:25.1.0的Android 4.0模拟器上,这并没有起作用。 - Peterdk
4
AppCompatImageView不能在小部件内使用。请在ImageView上使用setColorFilter。 - Massimo

46

您可以通过源代码来实现这一点。 以前, DrawableCompat 不支持着色。 从支持库22.1开始,您可以这样做,但需要按照以下方式进行:

您可以通过源代码来实现这一点。 以前, DrawableCompat 不支持着色。 从支持库22.1开始,您可以这样做,但需要按照以下方式进行:

<code>Drawable normalDrawable = getResources().getDrawable(R.drawable.drawable_to_tint);
Drawable wrapDrawable = DrawableCompat.wrap(normalDrawable);
DrawableCompat.setTint(wrapDrawable, getResources().getColor(R.color.colorPrimaryLight));
</code>

18
如果你需要在 API 版本小于 21 的设备上支持着色功能,那么你可能需要使用 ContextCompat.getColor() 而不是 getResources().getColor() - Sevastyan Savanyuk

22

你不能仅使用ImageView来显示你的Drawable吗?android:tint在旧的API级别上工作得很好。

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_calendar"
    android:tint="@color/primary"
    />

2
我正在使用ImageView来显示图标。这些图标是导航抽屉中元素的一部分。选择导航抽屉中的项目会有不同的颜色,因此我为每个图标创建了着色,并为每个图标创建了选择器。我正在使用该选择器来显示我的图标。选择器: - MaTTo
@Orochi 请看一下我的回答,它直接来自谷歌的博客。它主要只适用于Android 5.0+,但可能适用于某些运行在Android 5.0之前的设备上的小部件。 - Jared Burrows
那么,我应该自己给图标上色并将其放入我的drawable文件夹中吗? - MaTTo
1
你可以使用ImageView,把图标尽量设置成白色,并用 iv.setColorFilter(yourColor, Mode.Multiply) 设置任何颜色。确保导入 android.graphics.PorterDuff.Mode。 - jb15613
3
我也遇到了同样的问题。不幸的是,在 API<21的情况下,使用选择器的色调无法正常工作。 - Luccas
显示剩余2条评论

17

此前已经有类似的问题在这里提出:https://dev59.com/518d5IYBdhLWcg3wtz8Q#26533340

Android 5.0+(API 21+)才支持Android Drawable Tinting(它说:“此时此刻,该功能仅限于为动作栏和一些小部件着色。”)。

主题设置

...

当您设置这些属性时,AppCompat会自动将它们的值传播到API 21+上的框架属性。 这会自动为状态栏和概览(最近的任务条目)着色。

在旧版平台上,AppCompat在可能的情况下模拟颜色主题。目前,这仅限于为操作栏和一些小部件着色。

还有

小部件着色

在运行Android 5.0的设备上,所有小部件都使用我们刚刚讨论过的颜色主题属性进行着色。 在Lollipop上有两个主要特性允许这样做:drawable tinting和在绘制中引用主题属性(形式为?attr/foo)。

AppCompat在早期版本的Android中为UI小部件的子集提供了类似的行为:

AppCompat工具栏(操作模式等)提供的所有内容 EditText Spinner CheckBox RadioButton Switch(使用新的android.support.v7.widget.SwitchCompat) CheckedTextView 您无需进行任何特殊操作即可使其正常工作,只需像往常一样在布局中使用这些控件,AppCompat会自动处理剩余部分(有一些注意事项,请参阅下面的FAQ)。

来源:

http://android-developers.blogspot.com/2014/10/appcompat-v21-material-design-for-pre.html

https://chris.banes.me/2014/10/17/appcompat-v21/


为什么这被踩了?这是官方文档上的内容。 - Jared Burrows
答案已过时。ImageView现在也通过AppCompat支持android:tint,就像@Jonik的答案中所述。 - Vicky Chijwani
@VickyChijwani 提交一次编辑。你的意思是 AppCompatImageView,而不是 ImageView - Jared Burrows

13
现在,AppCompatImageView和AppCompatButton将替换ImageView和Button以支持在API较低的设备上进行着色。请查看链接以获取更多详细信息。AppCompatImageViewAppCompatButton

6

想要对图像进行着色,可以使用imageView.setColorFilter(int color)。这个方法适用于 API 8 及以上版本,并且可以将黑色图像着色成所需颜色。这个方法可以替代setImageTintList(),但是只使用android:tint应该也可以实现。


5

请使用以下命名空间
xmlns:app="http://schemas.android.com/apk/res-auto"

然后您可以将每个android:tint替换为app:tint。这样可以解决我的问题。


4
我有点晚了,但这就是如何做到的。
val textInput = EditText(context)

val drawable = ContextCompat.getDrawable(context, R.drawable.your_drawable)
drawable?.let {
    myDrawable -> DrawableCompat.setTint(myDrawable, ContextCompat.getColor(context, R.color.your_color))
    textInput.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, myDrawable, null)
}

1

这将按照您的要求进行操作,并且应该适用于支持库的所有Android版本:

Kotlin:

    @JvmStatic
    fun getTintedDrawable(inputDrawable: Drawable, @ColorInt color: Int): Drawable {
        val wrapDrawable = DrawableCompat.wrap(inputDrawable.mutate())
        DrawableCompat.setTint(wrapDrawable, color)
        DrawableCompat.setTintMode(wrapDrawable, Mode.SRC_IN)
        return wrapDrawable
    }

Java:

    public static Drawable getTintedDrawable(Drawable inputDrawable, @ColorInt int color) {
        Drawable wrapDrawable = DrawableCompat.wrap(inputDrawable.mutate());
        DrawableCompat.setTint(wrapDrawable, color);
        DrawableCompat.setTintMode(wrapDrawable, PorterDuff.Mode.SRC_IN);
        return wrapDrawable;
    }

我喜欢这个答案。对于SDK < 21,需要使用mutate()wrap。对于SDK >= 21,这些函数将只返回inputDrawable,因此使用这些函数没有成本。此外,在包装后,实际上可以直接从wrapDrawable调用setTintsetTintMode,而无需使用DrawableCompat的静态方法。 - Chester Fung
@ChesterFung 如果SDK>=21,它应该是什么样子的(请在回答之前进行检查)? - android developer

1
如果有人想创建新的可绘制对象(tin1,tint2等),请尝试以下方法。
<?xml version="1.0" encoding="utf-8"?>
<bitmap
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:src="@drawable/your_image"
  android:tint="@color/tint_color">
   </bitmap>

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