浅谈 Android L 的 Tint(着色)

Android L Tint 功能解析
本文详细介绍了Android L中Tint(着色)功能的工作原理。通过使用Tint,开发者能够轻松改变应用中控件的颜色,从而避免重复制作资源图片,有效减小APK文件体积。文章还深入探讨了Tint的实现方式,并提供了源码级别的解析。

原文地址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0128/3923.html

浅谈 Android L 的 Tint(着色)


Tint 是什么?

Tint 翻译为着色。

着色,着什么色呢,和背景有关?当然是着背景的色。当我们开发 App 的时候,如果使用了 Theme.AppCompat 主题的时候,会发现 ActionBar 或者 Toolbar 及相应的控件的颜色会相应的变成我们在 Theme 中设置的 colorPrimary, colorPrimaryDark, colorAccent 这些颜色,这是为什么呢,这就全是 Tint 的功劳了!

这样做有什么好处呢?好处就是你不必再老老实实的打开 PS 再制作一张新的资源图。而且大家都知道 apk 包最大的就是图片资源了,这样减少不必要资源图,可以极大的减少了我们的 apk 包的大小。

实现的方式就是用一个颜色为我们的背景图片设置 Tint(着色)。

例子:

WhiteBall-2WhiteBall-1

大家可以看上面再张图,这个是做的一个应用“小白球”,如图 1 的小图片本来都是白色的(圆背景的单独设的),但是经过 Tint 着色后就变成了图 2 中的浅蓝色了。

好了,既然理解了tint的含义,我们赶紧看下这一切是如何实现的吧。 
其实底层特别简单,了解过渲染的同学应该知道PorterDuffColorFilter这个东西,我们使用SRC_IN的方式,对这个Drawable进行颜色方面的渲染,就是在这个Drawable中有像素点的地方,再用我们的过滤器着色一次。 
实际上如果要我们自己实现,只用获取View的backgroundDrawable之后,设置下colorFilter即可。

看下最核心的代码就这么几行

if (filter == null) { 
   // Cache miss, so create a color filter and add it to the cache 
   filter = new PorterDuffColorFilter(color, mode); 
}

d.setColorFilter(filter); 
通常情况下,我们的mode一般都是SRC_IN,如果想了解这个属性相关的资料,这里是传送门:http://blog.youkuaiyun.com/t12x3456/article/details/10432935 (中文)

由于API Level 21以前不支持background tint在xml中设置,于是提供了ViewCompat.setBackgroundTintList方法和ViewCompat.setBackgroundTintMode用来手动更改需要着色的颜色,但要求相关的View继承TintableBackgroundView接

源码解析

以 EditText 为例,其它的基本一致

public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(TintContextWrapper.wrap(context), attrs, defStyleAttr);

    ...

    ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1)); //根据背景的resource id获取内置的着色颜色。
    if (tint != null) {
        setInternalBackgroundTint(tint); //设置着色
    }

    ...
}

private void setInternalBackgroundTint(ColorStateList tint) {
    if (tint != null) {
        if (mInternalBackgroundTint == null) {
            mInternalBackgroundTint = new TintInfo();
        }
        mInternalBackgroundTint.mTintList = tint;
        mInternalBackgroundTint.mHasTintList = true;
    } else {
        mInternalBackgroundTint = null;
    }
    //上面的代码是记录tint相关的信息。
    applySupportBackgroundTint();  //对背景应用tint
}


 private void applySupportBackgroundTint() {
    if (getBackground() != null) {
        if (mBackgroundTint != null) {
            TintManager.tintViewBackground(this, mBackgroundTint);
        } else if (mInternalBackgroundTint != null) {
            TintManager.tintViewBackground(this, mInternalBackgroundTint); //最重要的,对tint进行应用
        }
    }
}

然后我们进入tintViewBackground看下TintManager里面的源码

 public static void tintViewBackground(View view, TintInfo tint) {
    final Drawable background = view.getBackground();
    if (tint.mHasTintList) {
        //如果设置了tint的话,对背景设置PorterDuffColorFilter
        setPorterDuffColorFilter(
                background,
                tint.mTintList.getColorForState(view.getDrawableState(),
                        tint.mTintList.getDefaultColor()),
                tint.mHasTintMode ? tint.mTintMode : null);
    } else {
        background.clearColorFilter();
    }

    if (Build.VERSION.SDK_INT

自定义控件的着色

public class AppCompatView extends View implements TintableBackgroundView {

    private TintInfo mTintInfo;

    public AppCompatView(Context context) {
        this(context, null);
    }

    public AppCompatView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AppCompatView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        EmBackgroundTintHelper.loadFromAttributes(this, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mTintInfo = new TintInfo();
        mTintInfo.mHasTintList = true;
    }

    @Override
    public void setSupportBackgroundTintList(ColorStateList tint) {
        EmBackgroundTintHelper.setSupportBackgroundTintList(this, mTintInfo,tint);
    }

    @Nullable
    @Override
    public ColorStateList getSupportBackgroundTintList() {
        return EmBackgroundTintHelper.getSupportBackgroundTintList(mTintInfo);
    }

    @Override
    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
        EmBackgroundTintHelper.setSupportBackgroundTintMode(this, mTintInfo, tintMode);
    }

    @Nullable
    @Override
    public PorterDuff.Mode getSupportBackgroundTintMode() {
        return EmBackgroundTintHelper.getSupportBackgroundTintMode(mTintInfo);
    }
}

最后

最后打个小广告,并附上 Github 及我的 Blog

WhiteBall


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值