Android Tab layout: Wrap tab indicator width with respect to tab title
Asked Answered
C

15

45

Is there any way to wrap tab indicator width with respect to tab title ?

Current

Required

Calvaria answered 8/11, 2016 at 6:53 Comment(0)
G
54

Yes, it's possible to wrap tab indicator as title setting padding to 0

 public void wrapTabIndicatorToTitle(TabLayout tabLayout, int externalMargin, int internalMargin) {
        View tabStrip = tabLayout.getChildAt(0);
        if (tabStrip instanceof ViewGroup) {
            ViewGroup tabStripGroup = (ViewGroup) tabStrip;
            int childCount = ((ViewGroup) tabStrip).getChildCount();
            for (int i = 0; i < childCount; i++) {
                View tabView = tabStripGroup.getChildAt(i);
                //set minimum width to 0 for instead for small texts, indicator is not wrapped as expected
                tabView.setMinimumWidth(0);
                // set padding to 0 for wrapping indicator as title
                tabView.setPadding(0, tabView.getPaddingTop(), 0, tabView.getPaddingBottom());
                // setting custom margin between tabs
                if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
                    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) tabView.getLayoutParams();
                    if (i == 0) {
                        // left
                        settingMargin(layoutParams, externalMargin, internalMargin);
                    } else if (i == childCount - 1) {
                        // right
                        settingMargin(layoutParams, internalMargin, externalMargin);
                    } else {
                        // internal
                        settingMargin(layoutParams, internalMargin, internalMargin);
                    }
                }
            }

            tabLayout.requestLayout();
        }
}

private void settingMargin(ViewGroup.MarginLayoutParams layoutParams, int start, int end) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        layoutParams.setMarginStart(start);
        layoutParams.setMarginEnd(end);
        layoutParams.leftMargin = start;
        layoutParams.rightMargin = end;
    } else {
        layoutParams.leftMargin = start;
        layoutParams.rightMargin = end;
    }
}

Result

EDIT: As of com.android.support:design:28.0.0, you can adjust the indicator as label easily now setting:

app:tabIndicatorFullWidth="false"

EDIT July 2019: Use the material dependency com.google.android.material:material:x.x.x

Giaimo answered 17/5, 2017 at 13:31 Comment(7)
thanks @leon, It worked for me using scrollable TabLayout (tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE))Kaoliang
This worked. Make sure you set the following in XML too: app:tabPadding="0dp"Overblown
Working fine for meKrell
What are the values for externalMargin and internalMargin? I have given as 0,0 its working only if tabMode = scrollable, not working for tabMode=fixedPestle
Its not working for me..I need solution.I am using tablayout <android.support.design.widget.TabLayout android:id="@+id/sub_tabs" android:layout_width="fill_parent" app:tabBackground="@color/bgdarkblue" app:tabTextColor="@color/colorWhite" app:tabIndicatorColor="@color/barColor" android:theme="@style/tabTheme" app:tabPadding="0dp" android:layout_height="48dp"/> I am using two tabs but still it fills half width....I dont knw why is it like this.Can u give some SolutionBichloride
Can we append two tabs indicators in one ?!Nadia
can someone help me out, to get indicator appear, for me underline indicator is not showing :(Struma
O
33

As of support library 28 you can do the following:

app:tabIndicatorFullWidth="false"
app:tabPaddingStart="25dp"
app:tabPaddingEnd="25dp"

You can set the desired padding that affects the tab indicator.

Also you can now do this:

app:tabIndicator="@drawable/tab_indicator"

this will set a custom drawable as indicator.

example of custom drawable:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="@color/colorPrimary"/>
    <corners android:radius="5dp"/>
</shape>
Onetoone answered 4/12, 2018 at 14:12 Comment(1)
How to set fixed with for tab indicator ? By using this attribute app:tabIndicatorFullWidth="false", tab indicator width is getting changed according to tab text width. I want to have fixed width for tab indicator, how can i achieve that ?Neral
A
12

enter image description here

If you don't need the strip to be smaller than the text, then this should work:

public static void reduceMarginsInTabs(TabLayout tabLayout, int marginOffset) {

    View tabStrip = tabLayout.getChildAt(0);
    if (tabStrip instanceof ViewGroup) {
        ViewGroup tabStripGroup = (ViewGroup) tabStrip;
        for (int i = 0; i < ((ViewGroup) tabStrip).getChildCount(); i++) {
            View tabView = tabStripGroup.getChildAt(i);
            if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
                ((ViewGroup.MarginLayoutParams) tabView.getLayoutParams()).leftMargin = marginOffset;
                ((ViewGroup.MarginLayoutParams) tabView.getLayoutParams()).rightMargin = marginOffset;
            }
        }

        tabLayout.requestLayout();
    }
}

For added points you could check the text size of each title.

Allcot answered 13/4, 2017 at 23:6 Comment(4)
@Vishvadave thanks for your feedback, can you provide any details about your setup?Allcot
Worked for me! Muchas gracias.Practically
hey , thanks this is working like a charm , but what if i want the indicator width to be lesser than the text ??Patti
Unfortunately not possible with this approach. Let me know if you figure out a way to do that.Allcot
P
7

One simple solution is:

tabLayout.setTabIndicatorFullWidth(false);

But it works with latest dependency like

implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:design:28.0.0'
Pedagogics answered 26/9, 2018 at 12:28 Comment(2)
How to set fixed with for tab indicator ? By using this attribute app:tabIndicatorFullWidth="false", tab indicator width is getting changed according to tab text width. I want to have fixed width for tab indicator, how can i achieve that ?Neral
Hi @KPradeepKumarReddy u can try with below attributes app:tabMaxWidth="0dp" app:tabGravity="fill" app:tabMode="fixed" Note: I didn't excute, so sorry in advance if above solution not workPedagogics
K
5

You can do this:

layout/layout_demo.xml

<com.google.android.material.tabs.TabLayout
    app:tabIndicatorFullWidth="false"
    app:tabIndicator="@drawable/tab_indicator"
    .../>

res/tab_indicator.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="center">
    <shape>
        <size
            android:width="51dp"
            android:height="3dp" />
    </shape>
</item>
</layer-list>
Kalagher answered 9/8, 2021 at 13:10 Comment(0)
C
4

Short answer is "no". Here is explanation.

There is private class SlidingTabStrip within TabLayout that draws indicator

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        // Thick colored underline below the current selection
        if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
            canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
                    mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
        }
    }

I believe mIndicatorLeft and mIndicatorRight is what you need. These fields are set in the same class:

    private void setIndicatorPosition(int left, int right) {
        if (left != mIndicatorLeft || right != mIndicatorRight) {
            // If the indicator's left/right has changed, invalidate
            mIndicatorLeft = left;
            mIndicatorRight = right;
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

where left and right parameters are calculated in next method:

private void updateIndicatorPosition() {
        final View selectedTitle = getChildAt(mSelectedPosition);
        int left, right;
        if (selectedTitle != null && selectedTitle.getWidth() > 0) {
            left = selectedTitle.getLeft();
            right = selectedTitle.getRight();
            if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
                // Draw the selection partway between the tabs
                View nextTitle = getChildAt(mSelectedPosition + 1);
                left = (int) (mSelectionOffset * nextTitle.getLeft() +
                        (1.0f - mSelectionOffset) * left);
                right = (int) (mSelectionOffset * nextTitle.getRight() +
                        (1.0f - mSelectionOffset) * right);
            }
        } else {
            left = right = -1;
        }
        setIndicatorPosition(left, right);
    }

And the worst thing is that SlidingTabStrip field in TabLayout is private and final.

private final SlidingTabStrip mTabStrip;

I don't see how it's possible to achieve what you need without creating totally new TabLayout.

Claudieclaudina answered 8/11, 2016 at 9:59 Comment(4)
When i set the gravity = fill and padding left and right to 12, the indicator stopped at the end of the second tab title. Didn't extend. If its possible to do so, won't there be a way to wrap the other sides to ? @StasMelnychenkoCalvaria
"When i set the gravity = fill and padding left and right to 12" - which properties do you mean here? TabLayout?Claudieclaudina
in the xml ' <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_toRightOf="@+id/loginback" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:layout_marginRight="20dp" _** android:paddingRight="12dp" app:tabGravity="fill" android:paddingLeft="12dp"** android:layout_marginLeft="40dp"/>'Calvaria
Changing padding of TabLayout means that you cut whole stripe, that contains indicator.Claudieclaudina
O
4

A simple solution is to add this attribute to your tabLayout

app:tabIndicatorFullWidth="false"
Orator answered 9/7, 2020 at 3:23 Comment(1)
How to set fixed with for tab indicator ? By using this attribute app:tabIndicatorFullWidth="false", tab indicator width is getting changed according to tab text width. I want to have fixed width for tab indicator, how can i achieve that ?Neral
S
1

You can do it using styles.

In the layout xml:

<android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        style="@style/AppTabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

in the styles.xml:

<style name="AppTabLayout" parent="Widget.Design.TabLayout">
    <item name="android:layout_marginLeft">40dp</item>
    <item name="android:layout_marginRight">40dp</item>
</style>
Substantialism answered 16/11, 2017 at 8:42 Comment(0)
M
0

I'm note sure about the SDK Version needed, but you can do it directly in your XML file, adding the padding attribute using app namespace.

Just make your TabLayout looks like this:

        <android.support.design.widget.TabLayout
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/tab"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ...
            app:tabPaddingEnd="0dp"
            app:tabPaddingStart="0dp"
        </android.support.design.widget.TabLayout>

You can move the xmlns:app="http://schemas.android.com/apk/res-auto" for your parent layou too, but its your choice :)

Thanks leon for his answer, that lead me to achieve that by XML.

Magnusson answered 19/9, 2017 at 16:18 Comment(0)
R
0

I tried 2 hours for various solutions but none of those have a perfect effect.
The problem lies on the tabView's padding - even if I set its padding to all 0,
Different tabs still have different padding and therefore the text inside its TextView varies in size.

Then I found this library which perfectly solved it.
https://github.com/H07000223/FlycoTabLayout
As @Stas Melnychenko said, we probably can't do it unless we re-write a TabLayout. And this library does re-write a TabLayout...

Rothstein answered 22/9, 2017 at 4:48 Comment(0)
W
0

There's a attribute called tabIndicatorFullWidth inside android.support.design.widget.TabLayout

<android.support.design.widget.TabLayout
    android:id="@+id/tablayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tabBackground="@color/colorPrimary"

    app:tabIndicatorFullWidth="false" // tested okay on 8.1 , 6.0.1 

    app:tabTextColor="@color/colorAccent"
    app:tabTextAppearance="@style/CampusTabText"
    android:minHeight="?attr/actionBarSize"
    app:tabMaxWidth="0dp"
    app:tabGravity="fill"
    app:tabMode="fixed" />
Wendling answered 1/9, 2018 at 8:53 Comment(1)
How to set fixed with for tab indicator ? By using this attribute app:tabIndicatorFullWidth="false", tab indicator width is getting changed according to tab text width. I want to have fixed width for tab indicator, how can i achieve that ?Neral
M
0

We can easily set the width of the Tab indicator with respect to its Tab Title. Because of the com.android.support:design:28.0.0, we can easily adjust it using below code in xml file:

app:tabIndicatorFullWidth="false"

Militant answered 29/11, 2021 at 6:55 Comment(0)
E
0

For me app:tabIndicatorFullWidth="false" works perfectly. Here is my full code

<com.google.android.material.tabs.TabLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabIndicator="@drawable/my_tab_indicator"
                app:tabIndicatorColor="@color/tab_selected_unselected"
                app:tabIndicatorGravity="bottom"
                app:tabIndicatorHeight="@dimen/_3sdp"
                app:tabRippleColor="@color/transparent"
                app:tabMode="fixed"
                app:tabGravity="fill"
                app:tabIndicatorFullWidth="false"
                app:tabSelectedTextColor="@color/bottom_navigation_menu_selected"
                app:tabTextAppearance="@style/TabItemTextAppearance"
                app:tabTextColor="@color/title_1">

                <com.google.android.material.tabs.TabItem
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="অবজেক্টিভ" />

                <com.google.android.material.tabs.TabItem
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="সাবজেক্টিভ" />


</com.google.android.material.tabs.TabLayout>
Eucharis answered 15/12, 2021 at 8:39 Comment(0)
R
0

You just need to add app:tabMode="scrollable". It automatically resizes

Ruelas answered 8/3, 2022 at 13:11 Comment(0)
F
-2

Wrap tab indicator width with respect to tab title and add right padding with tab title and right margin with tab indicator.

public class TabLayoutActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);

        TabLayout tabs = (TabLayout) findViewById(R.id.tabs);

        tabs.setTabTextColors(Color.parseColor("#727272"), Color.parseColor("#305b0d"));


        ViewPager pager = (ViewPager) findViewById(R.id.pager);
        TabsPagerAdapter adapter = new TabsPagerAdapter(getSupportFragmentManager());

        pager.setAdapter(adapter);
        tabs.setupWithViewPager(pager);

        wrapTabIndicatorToTitle(tabs,20,20);

    }
    public void wrapTabIndicatorToTitle(TabLayout tabLayout, int externalMargin, int internalMargin) {
        View tabStrip = tabLayout.getChildAt(0);
        if (tabStrip instanceof ViewGroup) {
            ViewGroup tabStripGroup = (ViewGroup) tabStrip;
            int childCount = ((ViewGroup) tabStrip).getChildCount();
            for (int i = 0; i < childCount; i++) {
                View tabView = tabStripGroup.getChildAt(i);


                //set minimum width to 0 for instead for small texts, indicator is not wrapped as expected
                tabView.setMinimumWidth(0);
                // set padding to 0 for wrapping indicator as title
                tabView.setPadding(0, tabView.getPaddingTop(), 40, tabView.getPaddingBottom());
                // setting custom margin between tabs
                if (tabView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
                    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) tabView.getLayoutParams();
                    if (i == 0) {
                        // left
                        setMargin(layoutParams, externalMargin, internalMargin);
                    } else if (i == childCount - 1) {
                        // right
                        setMargin(layoutParams, internalMargin, externalMargin);
                    } else {
                        // internal
                        setMargin(layoutParams, internalMargin, internalMargin);
                    }
                }
            }

            tabLayout.requestLayout();
        }
    }

    private void setMargin(ViewGroup.MarginLayoutParams layoutParams, int start, int end) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            layoutParams.setMarginStart(start);
            layoutParams.setMarginEnd(end);
        } else {
            layoutParams.leftMargin = start;
            layoutParams.rightMargin = end;
        }
    }
}
Fatma answered 11/6, 2018 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.