Android Drawable着色绘图

在Android开发中或许遇到过这样的情况:

  • 明明几张图片除了颜色不同以外,其他都一模一样。
  • 可是要用这些图片还得都放入项目里面去。
  • 如果还需要其他色的相同图片,那可就只能再制作再加入。

那有没有什么方案,可以代码动态着色,只放一张图就可以了呢?请接着往下看


为什么要用代码动态着色Drawable

文章开头其实已经给出了部分原因,以下是使用它的好处和提示

  • 适用于具有白色和透明颜色的图像。
  • 当应用程序支持主题时,不需要同一图标的多个drawables,这意味着apk占用更少的空间。
  • 完美契合谷歌的材料图标包,只需下载白色.png版本,并相应地色调。
  • 也是一个史诗般的调色板库
  • 如果与列表项一起使用,则缓存可绘制项。
  • 也可以在ToolBar中,如果以编程方式组装,而不是使用menu.xml。
  • 创建一个StateListDrawable具有来自同一图像的不同颜色的可绘制的多个状态,从而减小apk大小。

动态着色Drawable具体实现方案

Google V4包 方案

其实Google已经在 Android Support V4 包中向我们提供了 DrawableCompat 类。
它为Lollipop之前设备引入了着色功能。它有一个完整的API,甚至支持着色列表和RTL布局的镜像,
但它对我们的只改变整个颜色的需求来说有点重量,还必须用当前的wrap()方法包装当前的Drawable 。

  • java代码

    1
    2
    3
    4
    5
    6
    //Drawable着色方法
    public static Drawable setTintDrawable(Drawable drawable,ColorStateList colorStateList){
    Drawable newdrawable = DrawableCompat.wrap(drawable);
    DrawableCompat.setTintList(newdrawable,colorStateList);
    return newdrawable;
    }
  • 使用方法

    1
    imageView.setBackgroundDrawable(setTintDrawable(getResources().getDrawable(R.mipmap.ic_account_balance_black_24dp).mutate(), getResources().getColorStateList(R.color.tint_colors)));
  • tint_colors.xml

    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">  
    <item android:color="@color/red" android:state_pressed="true"/>  
      <item android:color="@color/green" />  
    </selector>
  • 注意事项!!!

    • Android 为了优化系统性能,资源 Drawable 只有一份拷贝,你修改了它,等于所有的都修改了。
    • 即:如果不使用 mutate() 方法,第二次不作任何处理使用上次着色的资源,这张资源图片颜色还会和上次一样。
    • Drawable 提供了mutate(),就是用来打破这种共享状态,等于就是要告诉系统,我要修改(mutate)这个 Drawable。
    • 你可能会有这样的担心,调用 mutate() 是不是在内存中把 Bitmap 拷贝了一份?其实不是这样的,还是公用的 Bitmap,只是拷贝了一份状态值,这个数据量很小,所以不用担心。
第三方TintedBitmapDrawable方案

一个名为TintedBitmapDrawable的轻量级BitmapDrawable子类,
使用一个覆盖的方法照顾使用LightingColorFilter的着色。
它只包含三个函数,因此它并不意味着方法计数的显着增加。
可以在两个额外构造函数之一中或通过方法指定色调颜色。draw()setTint()

  • java代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public final class TintedBitmapDrawable extends BitmapDrawable {
    private int tint;
    private int alpha;

    public TintedBitmapDrawable(final Resources res, final Bitmap bitmap, final int tint) {
    super(res, bitmap);
    this.tint = tint;
    this.alpha = Color.alpha(tint);
    }

    public TintedBitmapDrawable(final Resources res, final int resId, final int tint) {
    super(res, BitmapFactory.decodeResource(res, resId));
    this.tint = tint;
    this.alpha = Color.alpha(tint);
    }

    public void setTint(final int tint) {
    this.tint = tint;
    this.alpha = Color.alpha(tint);
    }

    @Override public void draw(final Canvas canvas) {
    final Paint paint = getPaint();
    if (paint.getColorFilter() == null) {
    paint.setColorFilter(new LightingColorFilter(tint, 0));
    paint.setAlpha(alpha);
    }
    super.draw(canvas);
    }
    }
  • 使用方法
    1
    tintedDrawable = new TintedBitmapDrawable(resources, R.drawable.ic_arrow_back_white_24dp, Color.GREEN);

总结

  • 两者实现方案,如果需求只是单纯改变资源颜色建议使用后者。
  • 特别强调,如果使用第一种方案,大家一定要看注意事项!!!

文章到此结束了,如果大家还有什么疑问、困惑,欢迎评论与我交流。