Tint 翻译为着色。git
着色,着什么色呢,和背景有关?固然是着背景的色。当咱们开发 App 的时候,若是使用了 Theme.AppCompat 主题的时候,会发现 ActionBar 或者 Toolbar 及相应的控件的颜色会相应的变成咱们在 Theme 中设置的 colorPrimary, colorPrimaryDark, colorAccent 这些颜色,这是为何呢,这就全是 Tint 的功劳了!github
这样作有什么好处呢?好处就是你没必要再老老实实的打开 PS 再制做一张新的资源图。并且你们都知道 apk 包最大的就是图片资源了,这样减小没必要要资源图,能够极大的减小了咱们的 apk 包的大小。app
实现的方式就是用一个颜色为咱们的背景图片设置 Tint(着色)。ide
你们能够看上面再张图,这个是作的一个应用“小白球”,如图 1 的小图片原本都是白色的(圆背景的单独设的),可是通过 Tint 着色后就变成了图 2 中的浅蓝色了。ui
好了,既然理解了tint的含义,咱们赶忙看下这一切是如何实现的吧。 其实底层特别简单,了解过渲染的同窗应该知道PorterDuffColorFilter这个东西,咱们使用SRC_IN的方式,对这个Drawable进行颜色方面的渲染,就是在这个Drawable中有像素点的地方,再用咱们的过滤器着色一次。 实际上若是要咱们本身实现,只用获取View的backgroundDrawable以后,设置下colorFilter便可。this
看下最核心的代码就这么几行.net
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.csdn.net/t12x3456/article/details/10432935 (中文)翻译
因为API Level 21之前不支持background tint在xml中设置,因而提供了ViewCompat.setBackgroundTintList方法和ViewCompat.setBackgroundTintMode用来手动更改须要着色的颜色,但要求相关的View继承TintableBackgroundView接code
以 EditText 为例,其它的基本一致xml
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 <= 10) { // On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter // has changed, so we need to force an invalidation view.invalidate(); } } private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) { if (mode == null) { // If we don't have a blending mode specified, use our default mode = DEFAULT_MODE; } // First, lets see if the cache already contains the color filter PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode); if (filter == null) { // Cache miss, so create a color filter and add it to the cache filter = new PorterDuffColorFilter(color, mode); COLOR_FILTER_CACHE.put(color, mode, filter); } // 最最重要,原来是对background drawable设置了colorFilter 完成了咱们要的功能。 d.setColorFilter(filter); } private void applySupportBackgroundTint() { if (getBackground() != null) { if (mBackgroundTint != null) { TintManager.tintViewBackground(this, mBackgroundTint); } else if (mInternalBackgroundTint != null) { TintManager.tintViewBackground(this, mInternalBackgroundTint); //最重要的,对tint进行应用 } } }
以上是对API21如下的兼容。 若是咱们要实现本身的AppCompat组件实现tint的一些特性的话,咱们就能够指定好ColorStateList,利用TintManager对本身的背景进行着色,固然须要对外开放设置的接口的话,咱们还要实现TintableBackgroundView接口,而后用ViewCompat.setBackgroundTintList进行设置,这样能完成对v7以上全部版本的兼容。
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
Blog:http://imli.me