Talking about Tint in Android Material Design

Talking about Tint in Android Material Design

What is Tint

When I first came across the word Tint, I actually didn’t quite understand its meaning, and I wasn’t clear on the purpose of Google inventing it. It is usually used in conjunction with Background, but now that we already have Background, why do we still need Tint?

Tint translates to coloring.

Coloring, what color? It is related to the background, of course, the background color. When I develop a client and use the appcompat-v7 package, in order to achieve the effect of Material Design, we will set several colors in the theme, such as primaryColor, colorControlNormal, colorControlActived, etc., and some components we use, such as EditText, will automatically change to the background color we want. In the case of only one background image, this approach greatly reduces the size of our apk package.

The way to do this is to tint our background image with a color.
example:

Looking at the upcoming SegmentFault for Android 2.7, the issue feature, the color of this EditText is the same as our main color. It uses the TintManager class to color its background (green).
So what does this original graph look like? We found this graph from the appcompat-v7 package, which is a .9 graph and looks like this:


In fact, it is just a black bar, which is turned into a green bar by green coloring. It is this design method that saves us a lot of resource files in Material Design!

Okay, now that we understand the meaning of tint, let’s take a look at how all this is achieved.
In fact, the underlying layer is very simple. Those who have learned about rendering should know about PorterDuffColorFilter. We use the SRC_IN method to render the color of this Drawable, that is, where there are pixels in this Drawable, we use our filter to color it again.
In fact, if we want to implement it ourselves, we only need to get the backgroundDrawable of View and set the colorFilter.

Look at the core code, there are only a few lines

  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.  
  6. d.setColorFilter(filter);

Normally, our mode is usually SRC_IN. If you want to know more about this attribute, here is the portal: http://blog.csdn.net/t12x3456/article/details/10432935 (Chinese)

Because API Level 21 and before did not support background tint settings in XML, the ViewCompat.setBackgroundTintList method and ViewCompat.setBackgroundTintMode were provided to manually change the color that needs to be tinted, but the related View was required to inherit the TintableBackgroundView interface.
Source code analysis

Let's take a look at how the source code is implemented. Let's take AppCompatEditText as an example:
Look at the constructor (ignoring irrelevant code)

  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 )); //Get the built-in tinting color based on the background's resource id.  
  7. if (tint != null ) {
  8. setInternalBackgroundTint(tint); //Set tint  
  9. }
  10.  
  11. ...
  12. }
  13.  
  14. private   void setInternalBackgroundTint(ColorStateList tint) {
  15. if (tint != null ) {
  16. if (mInternalBackgroundTint == null ) {
  17. mInternalBackgroundTint = new TintInfo();
  18. }
  19. mInternalBackgroundTint.mTintList = tint;
  20. mInternalBackgroundTint.mHasTintList = true ;
  21. } else {
  22. mInternalBackgroundTint = null ;
  23. }
  24. //The above code records tint related information.  
  25. applySupportBackgroundTint(); //Apply tint to the background  
  26. }
  27.  
  28.  
  29. private   void applySupportBackgroundTint() {
  30. if (getBackground() != null ) {
  31. if (mBackgroundTint != null ) {
  32. TintManager.tintViewBackground( this , mBackgroundTint);
  33. } else   if (mInternalBackgroundTint != null ) {
  34. TintManager.tintViewBackground( this , mInternalBackgroundTint); //Most importantly, apply tint  
  35. }
  36. }
  37. }
  38.  
  39. Then we go to tintViewBackground and look at the source code in TintManager
  40.  
  41. public   static   void tintViewBackground(View view, TintInfo tint) {
  42. final Drawable background = view.getBackground();
  43. if (tint.mHasTintList) {
  44. //If tint is set, set PorterDuffColorFilter for the background  
  45. setPorterDuffColorFilter(
  46. background,
  47. tint.mTintList.getColorForState(view.getDrawableState(),
  48. tint.mTintList.getDefaultColor()),
  49. tint.mHasTintMode ? tint.mTintMode : null );
  50. } else {
  51. background.clearColorFilter();
  52. }
  53.  
  54. if (Build.VERSION.SDK_INT <= 10 ) {
  55. // On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter  
  56. // has changed, so we need to force an invalidation  
  57. view.invalidate();
  58. }
  59. }
  60.  
  61.  
  62. private   static   void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
  63. if (mode == null ) {
  64. // If we don't have a blending mode specified, use our default  
  65. mode = DEFAULT_MODE;
  66. }
  67.  
  68. // First, lets see if the cache already contains the color filter  
  69. PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
  70.  
  71. if (filter == null ) {
  72. // Cache miss, so create a color filter and add it to the cache  
  73. filter = new PorterDuffColorFilter(color, mode);
  74. COLOR_FILTER_CACHE.put(color, mode, filter);
  75. }
  76.  
  77. // Most importantly, it turns out that setting colorFilter on the background drawable completes the function we want.  
  78. d.setColorFilter(filter);
  79. }

The above is compatible with API 21 and below.
If we want to implement our own AppCompat component to implement some features of tint, we can specify the ColorStateList and use TintManager to color our background. Of course, if we need to open the setting interface to the outside world, we also need to implement the TintableBackgroundView interface and then use ViewCompat.setBackgroundTintList to set it. This will achieve compatibility with all versions above v7.
Examples

For example, if I want to implement Tint support for a custom component, I only need to inherit it and add some code. The code is as follows (almost universal):

  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.  
  15. public AppCompatFlowLayout(Context context, AttributeSet attributeSet) {
  16. this (context, attributeSet, 0 );
  17. }
  18.  
  19. public AppCompatFlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
  20. super (context, attributeSet, defStyle);
  21.  
  22. if (TintManager.SHOULD_BE_USED) {
  23. TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attributeSet,
  24. TINT_ATTRS, defStyle, 0 );
  25. if (a.hasValue( 0 )) {
  26. ColorStateList tint = a.getTintManager().getTintList(a.getResourceId( 0 , - 1 ));
  27. if (tint != null ) {
  28. setInternalBackgroundTint(tint);
  29. }

<<:  Ten days after the Android vulnerability broke out, it changed Google and Samsung

>>:  Remember! Do not perform time-consuming operations in the UI main thread

Recommend

Global Android phone shipments decline for the first time

[[127717]] 1. Global shipments of Android phones ...

I am neither a turtle nor a stinky lady. Cover your eyes and guess who I am?

Flowers smile to birds, frogs sing to willows, wa...

How to use referrals to acquire customers at low cost in online education

Different from fission, referral refers to the be...

Outrageous! These animals use their noses to walk and their ears to fly!

Not long ago, a shocking news came out from the C...

Google releases x86 64-bit Android L emulator

The video, created by Terence Tuhinanshu, shows a...

Detail page copywriting template with conversion rate over 85%!

Copywriting has always been a very popular job : ...

The correct way to “value” social apps

“This is a social APP that will subvert WeChat!” ...

How to make 100,000 RMB a month with Douyin local food accounts

I think many people thought when they were young ...

10 tips for KOL marketing!

As a person who has been working in the market fo...

Even with the advent of giant screens, iPhone sales are still soaring

Apple's iPhone sales for the quarter will be m...

Good operations must have strong persuasiveness. Here are three tips for you.

Whether in our daily life or work, dealing with p...