demand

Implement the following UI effect with an indicator width of 10dp, height of 4DP, and rounded corners of 2DP

Common practices are as follows:

  • Define the indicator tab_indicator.xml

    
            
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:width="10dp"
            android:height="4dp"
            android:gravity="center">
            <shape>
                <corners android:radius="2dp" />
            </shape>
        </item>
    </layer-list>
    Copy the code
  • Define TabLayout and set app:tabIndicator=”@drawable/tab_indicator”

<com.google.android.material.tabs.TabLayout
     android:id="@+id/tabLayout"
     android:layout_width="wrap_content"
     android:layout_height="45dp"
     android:layout_gravity="center"
     app:tabIndicator="@drawable/tab_indicator"
     app:tabIndicatorColor="#3F51B5"
     app:tabIndicatorFullWidth="false"
     app:tabIndicatorHeight="4dp"
     app:tabMode="scrollable"
     app:tabRippleColor="@android:color/transparent"
     app:tabSelectedTextColor="# 333333"
     app:tabTextColor="# 999999" />

Copy the code

The display effect on Android 6.0 and above is as follows:

It looks like this on an Android 5.1 machine:

On Android 5.1, the width of the indicator is the same as the width of the text, rather than the 10DP we wanted

Troubleshoot problems

Android :width/height under the layer-list in the XML file does not work under Android 6.0. When we place the cursor in the tab_indicator. Attribute width is only used in API level 23 and higher (current min is 21)

The solution

Dynamically set the indicator width in the code and add a TabLayout extension method as follows:

/** * sets the indicator to a fixed width */
fun TabLayout.setSelectedTabIndicatorFixWidth(width: Float) {
    setSelectedTabIndicator(object : DrawableWrapper(tabSelectedIndicator) {
        override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
            var realLeft = left
            var realRight = right
            if((right - left).toFloat() ! = width) {val center = left + (right - left).toFloat() / 2
                realLeft = (center - width / 2).toInt()
                realRight = (center + width / 2).toInt()
            }
            super.setBounds(realLeft, top, realRight, bottom)
        }
    })
}
Copy the code

use

tab_indicator.xml


      
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="2dp" />
</shape>
Copy the code

Layout file

<com.google.android.material.tabs.TabLayout
     android:id="@+id/tabLayout"
     android:layout_width="wrap_content"
     android:layout_height="45dp"
     android:layout_gravity="center"
     app:tabIndicator="@drawable/tab_indicator"
     app:tabIndicatorColor="#3F51B5"
     app:tabIndicatorFullWidth="false"
     app:tabIndicatorHeight="4dp"
     app:tabMode="scrollable"
     app:tabRippleColor="@android:color/transparent"
     app:tabSelectedTextColor="# 333333"
     app:tabTextColor="# 999999" />
Copy the code
tabLayout.setSelectedTabIndicatorFixWidth(dp2px(10f))
Copy the code

The effect

  • Android 5.0 & 5.1

  • The Android 6.0 +

reference

TabLayout a line of code with a fixed width