谈谈AndroidMaterialDesign中的Tint

什么是Tint

成都创新互联总部坐落于成都市区,致力网站建设服务有网站建设、成都网站设计、网络营销策划、网页设计、网站维护、公众号搭建、小程序制作、软件开发等为企业提供一整套的信息化建设解决方案。创造真正意义上的网站建设,为互联网品牌在互动行销领域创造价值而不懈努力!

当我开始接触Tint这个词的时候,其实是蛮不理解它的意思的,以及并不清楚Google发明它的目的,它一般搭配Background配合使用,但是现在已经有了Background,为什么还需要Tint呢?

Tint 翻译为着色。

着色,着什么色呢?和背景有关,当然是着背景的色。当我开发客户端,使用了appcompat-v7包的时候,为了实现Material Design的效果,我们会去设置主题里的几个颜色,重要的比如primaryColor,colorControlNormal,colorControlActived 等等,而我们使用的一些组件,比如EditText就会自动变成我们想要的背景颜色,在背景图只有一张的情况下,这样的做法极大的减少了我们apk包的大小。

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

看看即将发布的SegmentFault for Android 2.7中,发布问题功能,这个EditText的颜色和我们的主要颜色相同。它利用了TintManager这个类,为自己的背景进行着色(绿色)。
那么这个原始图是什么样子呢?我们从appcompat-v7包中找到了这个图,是一个.9图,样子如下:

其实它只是一个黑色的条,通过绿色的着色,变成了一个绿色的条。 就是这样的设计方式,使得我们在Material Design中省了多少资源文件呀!

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

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

 
 
 
 
  1. if (filter == null) { 
  2. // Cache miss, so create a color filter and add it to the cache 
  3. filter = new PorterDuffColorFilter(color, mode); 
  4.  
  5. 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接口。
源码解析

看下源码是如何实现的吧,我们以AppCompatEditText为例:
看下构造函数(省略无关代码)

 
 
 
 
  1. public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) { 
  2. super(TintContextWrapper.wrap(context), attrs, defStyleAttr); 
  3.  
  4. ... 
  5.  
  6. ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1)); //根据背景的resource id获取内置的着色颜色。 
  7. if (tint != null) { 
  8. setInternalBackgroundTint(tint); //设置着色 
  9.  
  10. ... 
  11.  
  12. private void setInternalBackgroundTint(ColorStateList tint) { 
  13. if (tint != null) { 
  14. if (mInternalBackgroundTint == null) { 
  15. mInternalBackgroundTint = new TintInfo(); 
  16. mInternalBackgroundTint.mTintList = tint; 
  17. mInternalBackgroundTint.mHasTintList = true; 
  18. } else { 
  19. mInternalBackgroundTint = null; 
  20. //上面的代码是记录tint相关的信息。 
  21. applySupportBackgroundTint(); //对背景应用tint 
  22.  
  23.  
  24. private void applySupportBackgroundTint() { 
  25. if (getBackground() != null) { 
  26. if (mBackgroundTint != null) { 
  27. TintManager.tintViewBackground(this, mBackgroundTint); 
  28. } else if (mInternalBackgroundTint != null) { 
  29. TintManager.tintViewBackground(this, mInternalBackgroundTint); //最重要的,对tint进行应用 
  30.  
  31. 然后我们进入tintViewBackground看下TintManager里面的源码 
  32.  
  33. public static void tintViewBackground(View view, TintInfo tint) { 
  34. final Drawable background = view.getBackground(); 
  35. if (tint.mHasTintList) { 
  36. //如果设置了tint的话,对背景设置PorterDuffColorFilter 
  37. setPorterDuffColorFilter( 
  38. background, 
  39. tint.mTintList.getColorForState(view.getDrawableState(), 
  40. tint.mTintList.getDefaultColor()), 
  41. tint.mHasTintMode ? tint.mTintMode : null); 
  42. } else { 
  43. background.clearColorFilter(); 
  44.  
  45. if (Build.VERSION.SDK_INT <= 10) { 
  46. // On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter 
  47. // has changed, so we need to force an invalidation 
  48. view.invalidate(); 
  49.  
  50.  
  51. private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) { 
  52. if (mode == null) { 
  53. // If we don't have a blending mode specified, use our default 
  54. mode = DEFAULT_MODE; 
  55.  
  56. // First, lets see if the cache already contains the color filter 
  57. PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode); 
  58.  
  59. if (filter == null) { 
  60. // Cache miss, so create a color filter and add it to the cache 
  61. filter = new PorterDuffColorFilter(color, mode); 
  62. COLOR_FILTER_CACHE.put(color, mode, filter); 
  63.  
  64. // 最最重要,原来是对background drawable设置了colorFilter 完成了我们要的功能。 
  65. d.setColorFilter(filter); 

以上是对API21以下的兼容。
如果我们要实现自己的AppCompat组件实现tint的一些特性的话,我们就可以指定好ColorStateList,利用TintManager对自己的背景进行着色,当然需要对外开放设置的接口的话,我们还要实现TintableBackgroundView接口,然后用ViewCompat.setBackgroundTintList进行设置,这样能完成对v7以上所有版本的兼容。
实例

比如我现在要对一个自定义组件实现对Tint的支持,其实只用继承下,加一些代码就好了,代码如下(几乎通用):

 
 
 
 
  1. public class AppCompatFlowLayout extends FlowLayout implements TintableBackgroundView { 
  2.  
  3. private static final int[] TINT_ATTRS = { 
  4. android.R.attr.background 
  5. }; 
  6.  
  7. private TintInfo mInternalBackgroundTint; 
  8. private TintInfo mBackgroundTint; 
  9. private TintManager mTintManager; 
  10.  
  11. public AppCompatFlowLayout(Context context) { 
  12. this(context, null); 
  13.  
  14. public AppCompatFlowLayout(Context context, AttributeSet attributeSet) { 
  15. this(context, attributeSet, 0); 
  16.  
  17. public AppCompatFlowLayout(Context context, AttributeSet attributeSet, int defStyle) { 
  18. super(context, attributeSet, defStyle); 
  19.  
  20. if (TintManager.SHOULD_BE_USED) { 
  21. TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attributeSet, 
  22. TINT_ATTRS, defStyle, 0); 
  23. if (a.hasValue(0)) { 
  24. ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1)); 
  25. if (tint != null) { 
  26. setInternalBackgroundTint(tint); 

网站名称:谈谈AndroidMaterialDesign中的Tint
地址分享:http://www.gawzjz.com/qtweb/news41/187341.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联