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