All-in-One — Android TabLayout and TabItem

Myrick Chow
ITNEXT
Published in
8 min readMay 31, 2019

--

TabLayout is a common widget working with ViewPager and ViewPager2. User can tap on each tap to go to a specific page in ViewPager directly. The most common example is the Google Play Store app.

Screenshot of Google Play Store app

In this article, I would share my experience on styling each TabItem. I found that the Google documentation does not synchronise with its opened source code. Reading the corresponding source code is the best way to understand the details of each attribute.

If you are interested in the implementation and tricks of ViewPager2, you could read more in my another article.

TabItem

TabItem is a View which contains an icon and a text. It has two states — selected and unselected. It must be “attached” to a TabLayout . However, according to the Google official documentation (see below quote) , it is a dummy View which is not actually “added” to a TabLayout. As a result, findViewById() will always return null and developer cannot customise each tab by referring to it directly.

TabItem is a special ‘view’ which allows you to declare tab items for a TabLayout within a layout. This view is not actually added to TabLayout, it is just a dummy which allows setting of a tab items's text, icon and custom layout. See TabLayout for more information on how to use it.

Reference: Google documentation about TabItem

If TabLayout is linked with a ViewPager2, we can manually use the callback TabLayoutMediator.OnConfigureTabCallback in class TabLayoutMediator to style each inflated TabItem. To know more about the implementation and explanation, please refer to my another article:

TabLayout

Screenshot at the Google documentation

TabLayout is aHorizontalScrollView which contains a list of TabItem. It layouts each TabItem just like a horizontal LinearLayout.

However, it is restricted to only have TabItem as its children. Runtime error will occur if it is not followed, see below error message:

java.lang.RuntimeException: Unable to start activity ComponentInfo{…}: android.view.InflateException: Binary XML file line #35: Only TabItem instances can be added to TabLayout

First of all, let’s have a look at the brief summary of attributes available for TabLayout.

According to Material Design opened source code, tabIndicatorHeight and tabSelectedTextColor are both currently deprecated but Google official documentation has not yet updated.

tabIndicatorHeight attribute at Material Component source code
tabSelectedTextColor attribute at Material Component source code

Here is a list of attributes that you can jump to directly:

  1. Tab indicator attributes
    app:tabIndicator — Replacement of app:tabIndicatorHeight
    app:tabIndicatorColor
    app:tabIndicatorGravity
    app:tabIndicatorFullWidth
    app:tabIndicatorAnimationDuration — Newly added by not shown at Google official documentation
  2. Background attributes
    app:tabBackground
    app:tabRippleColor
    app:tabUnboundedRipple
  3. Icon attributes
    app:tabIconTint
    app:tabIconTintMode
  4. Text attributes
    app:tabInlineLabel
    app:tabTextColor — Replacement of app:tabSelectedTextColor
    app:tabTextAppearance
  5. Position attributes
    app:tabContentStart
    app:tabPadding, app:tabPaddingStart | Top | End | Bottom
  6. Size attributes
    app:tabMinWidth & app: tabMaxWidth
  7. Position attributes
    app:tabGravity
  8. Mode attributes
    app:tabMode

Tab indicator attributes

1. app:tabIndicator

For configuring the height of tab indicator. The default height is 2dp.

Do not use app:tabIndicatorHeight to change the height of indicator since it is now deprecated.

app:tabIndicatorHeight is now deprecated. Instead, set the intrinsic size of the custom drawable provided to the tabIndicator attribute in order to change the indicator height. For example, this can be done by setting the <size> property in a <shape> resource.

Reference: Line 26–28 of Material Component source code

Code for setting tab indicator dimension
Drawable resource code for declaring height

2. app:tabIndicatorColor (Self-explanatory)

3. app:tabIndicatorGravity

To define the position of tab indicator.

4. app:tabIndicatorFullWidth

Option 1 — true (Default) : Width of indicator equals the width of TabItem

Option 2 — false: Width of indicator is determined by the width of text inside TabItem. Minimum with of indicator is set to be 24dp.

Default minimum width of indicator; Line 188–189 of Material Component source code

5. app:tabIndicatorAnimationDuration

To define the time used for the tab indicator to go from current TabItem to the selected TabItem. By default it is 300ms declared in Material Component source code.

Line 196 of Material Component source code

Background attributes

1. app:tabBackground

There are two states for TabItem — Selected & default. Background can be a drawable state list.

2. app:tabRippleColor (Self-explanatory)

3. app:tabUnboundedRipple

Option 1 — false (default): Ripple is bounded by the boundary of its TabItem

Option 2 — true: Ripple grows beyond regardless the boundary of its TabItem

Icon attributes

1. app:tabIconTint

Icon tint is a layer of color which is painted on the non-transparent part of icon image.

2. app:tabIconTintMode

There are 6 tint modes and each one manipulates the transparency and color by different equations. It is not recommended to use this attribute unless you clearly understand what the calculation is.

Reference: PorterDuff mode — Google

Text attributes

1. app:tabInlineLabel

Option 1 — false (default): Icon is placed above the text of TabItem

Option 2 — true: Icon is placed at the start of text of TabItem

2. app:tabTextColor

Text color can be set for both the selected and default state by color state list.

app:tabSelectedTextColor is now deprecated Instead, provide a ColorStateList to the app: tabTextColor attribute with a selected color set.

Reference: Line 76–77 of Material Component source code

3. app:tabTextAppearance

Styling text in each TabItem cannot be done directly by something like android:textSize or android:textStyle. TextAppearance is the only available way to style it. For more information about general TextAppearance, you could refer to the following article written by Nick Butcher@Google:

Position attributes

1. app:tabContentStart

This attribute offset the start position of TabLayout.

However, it is tricky since there is no explanation in Google official document and setting this attribute solely does not seem to have any effect. The only solution I can think of is reading the source code directly and see the implementation. Below are the screenshot from source code:

Default value of app:tabContentStart and app:tabMode
Implementation of app:tabContentStart depends on the app:tabMode

As you can see, tabContentStart attribute is only applicable when app:tabMode="scrollable | auto" but tabMode is set to fixed by default. This is the reason why most developers are frustrated about using this attribute.

2. app:tabPadding, app:tabPaddingStart | Top | End | Bottom (Self-explanatory)

Size attributes

1. app:tabMinWidth & app: tabMaxWidth

The minimum and maximum width applied to each tab. These attributes will override the tabGravity fill into center if the total width of all TabItem is smaller than the width of TabLayout.

Position attributes

1. app:tabGravity

Option 1 — fill (default): Occupy the whole width of TabLayout and each tab has equal width.

Option 2 — center: All tabs are centered within TabLayout and each tab has the same width as that of the widest tab.

Mode attributes

1. app:tabMode

Option 1 — fixed: Display all tab at the same time and each tab has equal width which is based on the widest tab label.

Option 2 — scrollable: A horizontally scrollable TabLayout and each tab has its own width calculated by the dimension of its text and icon.

Option 3 — auto: either scrollable or fixed with center-gravity

There are two cases:

Case 1: When TabLayout is able to fit all TabItems within its bound, TabLayout is the same as:

Case 2: When TabLayout is NOT able to fit all TabItems within its bound, TabLayout is under MODE_SCROLLABLE.

Conclusion

TabLayout and TabItem are important UI for enhancing UX of a ViewPager. However, the styling of TabItem is a little bit difficult due to the lack of clear explanation and description at Google official documentation.

  1. TabItem is a dummy View which is not added to TabLayout and thus developer cannot reference it by using findViewById().
  2. TabLayout is a HorizontalScrollView and layouts its child horizontally by default.
  3. app:tabIndicatorHeight is deprecated and replaced by app:tabIndicator.
  4. app:tabSelectedTextColor is deprecated and replaced by app:tabTextColor with ColorStateList.
  5. app:tabIndicatorAnimationDuration is newly added but there are no official documentation about this in Google.

Please follow me at Twitter@myrick_chow for more information. Thank you for reading this article. Have a nice day! 😄

--

--

Mobile Lead @REAL Messenger Inc. https://real.co Focus on Android & iOS Native programming.