All-in-One — Android TabLayout and TabItem
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.
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
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.
Here is a list of attributes that you can jump to directly:
- Tab indicator attributes
app:tabIndicator — Replacement ofapp:tabIndicatorHeight
app:tabIndicatorColor
app:tabIndicatorGravity
app:tabIndicatorFullWidth
app:tabIndicatorAnimationDuration — Newly added by not shown at Google official documentation - Background attributes
app:tabBackground
app:tabRippleColor
app:tabUnboundedRipple - Icon attributes
app:tabIconTint
app:tabIconTintMode - Text attributes
app:tabInlineLabel
app:tabTextColor — Replacement ofapp:tabSelectedTextColor
app:tabTextAppearance - Position attributes
app:tabContentStart
app:tabPadding, app:tabPaddingStart | Top | End | Bottom - Size attributes
app:tabMinWidth & app: tabMaxWidth - Position attributes
app:tabGravity - 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
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.
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.
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.
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:
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 TabItem
s within its bound, TabLayout
is the same as:
Case 2: When TabLayout
is NOT able to fit all TabItem
s 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.
TabItem
is a dummyView
which is not added toTabLayout
and thus developer cannot reference it by usingfindViewById()
.TabLayout
is aHorizontalScrollView
and layouts its child horizontally by default.app:tabIndicatorHeight
is deprecated and replaced byapp:tabIndicator
.app:tabSelectedTextColor
is deprecated and replaced byapp:tabTextColor
with ColorStateList.app:tabIndicatorAnimationDuration
is newly added but there are no official documentation about this in Google.
Further readings:
- Google official documentation — TabLayout
- Material Design — TabLayout
- GitHubMaterial Components Android TabLayout source code
- GitHub Material Components Android TabLayout styles
- GitHub Material Components Android TabLayout attributes
- Setup between
TabLayout
andViewPager2
7. It is common to set ViewPager
to have horizontal margin while keeping the previous and next pages visible to user. However, the function is missing in ViewPager2
and caching mechanism is disabled by default. Let’s see how we can fix this issue here.
8. Dot-styled TabItem
is not officially documented. The process is concluded to only 4 simple steps.
Please follow me at Twitter@myrick_chow for more information. Thank you for reading this article. Have a nice day! 😄