In the work, often encounter the label bar indicating the width of the line, do the same as the width of the text, or even shorter than the text width of the design. Using TabLayout we can quickly implement a Material Design-style Tab bar, but the TabLayout Indicator by default fills a Tab and there is no direct way to change the width of the Indicator.
This article summarizes several options for changing Indicator’s width and discusses how to change it “elegantly”.
reflection
If you have a need to change the indicator line width in your project and have searched the Internet for a change method, you are probably using this method in your project. Most articles on the Internet are through the analysis of the source code, with reflection, the code is as follows:
public void setIndicatorWidth(@NonNull final TabLayout tabLayout, final int margin) {
tabLayout.post(new Runnable() {
@Override
public void run(a) {
try {
Field mTabStripField = tabLayout.getClass().getDeclaredField("mTabStrip");
mTabStripField.setAccessible(true);
LinearLayout mTabStrip = (LinearLayout) mTabStripField.get(tabLayout);
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
View tabView = mTabStrip.getChildAt(i);
Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");
mTextViewField.setAccessible(true);
TextView mTextView = (TextView) mTextViewField.get(tabView);
tabView.setPadding(0.0.0.0);
int width = mTextView.getWidth();
if (width == 0) {
mTextView.measure(0.0); width = mTextView.getMeasuredWidth(); } LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams(); params.width = width; params.leftMargin = margin; params.rightMargin = margin; tabView.setLayoutParams(params); tabView.invalidate(); }}catch (NoSuchFieldException e) {
e.printStackTrace();
} catch(IllegalAccessException e) { e.printStackTrace(); }}}); }Copy the code
This is fine, but if you upgrade the project SDK to 28 or above, it is no longer valid because the variable names in the TabLayout source code have been changed, so the code should look like this:
public void setIndicatorWidth(@NonNull final TabLayout tabLayout, final int margin) {
tabLayout.post(new Runnable() {
@Override
public void run(a) {
try {
Field slidingTabIndicatorField = tabLayout.getClass().getDeclaredField("slidingTabIndicator");
slidingTabIndicatorField.setAccessible(true);
LinearLayout mTabStrip = (LinearLayout) slidingTabIndicatorField.get(tabLayout);
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
View tabView = mTabStrip.getChildAt(i);
Field textViewField = tabView.getClass().getDeclaredField("textView");
textViewField.setAccessible(true);
TextView mTextView = (TextView) textViewField.get(tabView);
tabView.setPadding(0.0.0.0);
int width = mTextView.getWidth();
if (width == 0) {
mTextView.measure(0.0); width = mTextView.getMeasuredWidth(); } LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams(); params.width = width; params.leftMargin = margin; params.rightMargin = margin; tabView.setLayoutParams(params); tabView.invalidate(); }}catch (NoSuchFieldException e) {
e.printStackTrace();
} catch(IllegalAccessException e) { e.printStackTrace(); }}}); }Copy the code
This can be done with reflection, but I personally feel reflection is not elegant enough and it may not work with SDK upgrades.
The custom Tab
Tabs in TabLayout are customizable, but Indicators are not tabs.
So one solution is to hide the Indicator and add the Indicator line to the layout of the custom Tab.
We can hide Indicator by setting its color to transparent:
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@android:color/transparent" />
Copy the code
In the code, when a Tab is added, replace it with a custom Tab:
TabLayout.Tab tab = tabLayout.getTabAt(i);
tab.setCustomView(R.layout.layout_tab);
TextView tv = tab.getCustomView().findViewById(R.id.text_view);
tv.setText(tab.getText());
Copy the code
And also need to listen to the Tab switch, control the display of the indication line hidden:
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// TODO Tab is towed, refresh all tabs
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}@Override
public void onTabReselected(TabLayout.Tab tab) {}});Copy the code
In this way, any style can be implemented. One drawback, however, is that when tab-switching, there is no moving animation of the indicator line.
SDK 28+ Property configuration
If you are using SDK version 28 or above and need to change the Indicator width to match the text width, then great, now you just need to configure a TabLayout property:
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorFullWidth="false" />
Copy the code
When tabIndicatorFullWidth is set to false, the width of the Indicator will be the same as the width of the text, but this also means that the width of the Indicator will be different when the text width is different in different tabs, as shown below.
If the design requires the Indicator to be a fixed width, or shorter than the text, then we need to move on to other solutions.
Use the Drawable style
This last solution, which I think is the most elegant, is extremely simple to use. I haven’t seen anyone use it on the Internet, so it can be my original, haha.
Indicator allows us to set drawable to customize styles, like rounded corners. But regardless of the style, the Indicator is still full Tab width. It doesn’t matter, let’s make the background transparent and include a shape with a fixed width, like this:
<?xml version="1.0" encoding="utf-8"? >
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<item android:gravity="center">
<shape>
<size
android:width="16dp"
android:height="4dp" />
<corners android:radius="4dp" />
<solid android:color="@color/colorAccent" />
</shape>
</item>
</layer-list>
Copy the code
Then configure the tabIndicator property in the layout file:
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorHeight="10dp"
app:tabIndicator="@drawable/tab_indicator" />
Copy the code
It can also be set in code:
tabLayout.setSelectedTabIndicator(R.drawable.tab_indicator);
Copy the code
The effect is as follows:
As you can see from the above example, using this method you can visually increase the left and right margins of the Indicator as well as its top and bottom margins.
Well, in fact is to configure a Drawable file that simple, just found online a lot of people are asking, but haven’t people use this method to solve, here to share with you.