19

Is there a way to add margin between the tabs in a TabLayout? I've tried with using a custom style for Widget.Design.TabLayout, but there are properties only related to padding, but no margins.

Todor Kostov
  • 1,789
  • 1
  • 13
  • 20

7 Answers7

60

Ok mates, after spending 2-3 hours on that I finally found a solution.

If you are using TabLayout there is no way to add margins to the tabs by using styles and so on. (as @Connecting life with Android earlier)

But, you can do that by writing some Java code. All in all your code should look similar to that one:

            for(int i=0; i < mTabLayout.getTabCount(); i++) {
                View tab = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(i);
                ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) tab.getLayoutParams();
                p.setMargins(0, 0, 50, 0);
                tab.requestLayout();
            }

In order to get each and every tab as a View we have to first get the container which contains them. In this case the TabLayout is using a SlidingTabStrip as a container for the tabs. The SlidingTabStrip is the first child of the TabLayout:

View tab = ((ViewGroup) mTabLayout.getChildAt(0))

And after this small detail, everything is pretty straight forward.

Todor Kostov
  • 1,789
  • 1
  • 13
  • 20
15

Here's how I did it in pure xml.

dimens.xml:

<!-- Set this to 50% of what you want the actual space between the tabs to be. -->
<dimen name="tab_spacing_half">5dp</dimen> <!-- i.e., 10dp spacing between tabs. -->

Layout containing your TabLayout:

<android.support.design.widget.TabLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/tab_spacing_half"/>

Add this to your theme in styles.xml:

<item name="tabBackground">@drawable/tab_background</item>

drawable-nodpi\tab_background.xml:

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

    <item
        android:drawable="@drawable/tab_background_selected"
        android:state_selected="true" />

    <item
        android:drawable="@drawable/tab_background_unselected"
        android:state_selected="false"
        android:state_focused="false"
        android:state_pressed="false" />

</selector>

drawable-nodpi\tab_background_selected.xml:

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

    <item
        android:left="@dimen/tab_spacing_half"
        android:right="@dimen/tab_spacing_half"
        android:top="@dimen/tab_spacing_half"
        android:bottom="@dimen/tab_spacing_half">

        <shape
            android:shape="rectangle">

            <corners
                android:radius="@dimen/border_radius_small">
            </corners>

            <stroke
                android:width="@dimen/divider_height_thick"
                android:color="@color/white">
            </stroke>

        </shape>

    </item>

</layer-list>

...That's where the trick is. Effectively, wrapping your background shape in an item with "padding" according to your @dimen/tab_spacing_half value. And finally...

drawable-nodpi\tab_background_unselected.xml:

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

    <item
        android:left="@dimen/tab_spacing_half"
        android:right="@dimen/tab_spacing_half"
        android:top="@dimen/tab_spacing_half"
        android:bottom="@dimen/tab_spacing_half">

        <shape
            android:shape="rectangle">

            <corners
                android:radius="@dimen/border_radius_small">
            </corners>

            <stroke
                android:width="@dimen/divider_height_thin"
                android:color="@color/white">
            </stroke>

        </shape>

    </item>

</layer-list>
ban-geoengineering
  • 18,324
  • 27
  • 171
  • 253
9

Here is Kotlin version of @Todor Kostov's answer.

 for (i in 0 until applicationList_tabLayout.tabCount) {
            val tab = (applicationList_tabLayout.getChildAt(0) as ViewGroup).getChildAt(i)
            val p = tab.layoutParams as ViewGroup.MarginLayoutParams
            p.setMargins(10, 0, 10, 0)
            tab.requestLayout()
 }
Vadims Krutovs
  • 197
  • 2
  • 3
  • This is not working! – Kishan Solanki Feb 24 '22 at 14:15
  • @KishanSolanki It's working. The only thing is you need to place the code after setting the viewpager. Otherwise, there won't be any tabs set and the tab count will be 0. You need to give applicationList_tabLayout.tabCount - 1 also. – Anju Jun 10 '22 at 07:06
5

@Todor Kostov answered well, but the center of the tabs are slipped away because the last tab has margin too.

so use mTabLayout.getTabCount() - 1 instead of just mTabLayout.getCodeCount().

        for(int i=0; i < mTabLayout.getTabCount() - 1; i++) {
            View tab = ((ViewGroup) mTabLayout.getChildAt(0)).getChildAt(i);
            ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) tab.getLayoutParams();
            p.setMargins(0, 0, 50, 0);
            tab.requestLayout();
        }
Tura
  • 1,227
  • 11
  • 22
1

None of the answers was working for me! So here is the answer that will work for you and see the below result.

enter image description here

Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82
0

This is how set margin for four different tabs. You can change the setMargins(v1,v2,v3,v4) function values to get a suitable fitting for the number of tabs that you are working with. I hope this helps. Please note that tabHost is the object of TabHost hosting different tabs you are working with.

Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
View currentView;

for(int i=0; i<tabHost.getTabWidget().getChildCount(); i++) {
    //This code removes divider between tabs
    tabHost.getTabWidget().setDividerDrawable(null);
    tabHost.getTabWidget().getChildAt(i).setLayoutParams(new
        LinearLayout.LayoutParams((width / 8) - 2, 50));
    currentView = tabHost.getTabWidget().getChildAt(i);
    LinearLayout.LayoutParams currentLayout =
        (LinearLayout.LayoutParams) currentView.getLayoutParams();
    currentLayout.setMargins(30, 5, 80, 0);
}
Edwinfad
  • 515
  • 6
  • 14
0

Most suggestions here seems to suggest calling requestLayout() to update the margins. This will trigger a new layout pass. I also personally saw issues when animating the layout, in which the TabLayout was defined, causing the requestLayout to be called a bunch of times, and sometimes making the tabs "jump"

I think a better way of setting the margins between the tabs, would be to extend the TabLayout, and listen to when child views are added to the SlidingTabIndicator.

private const val YOUR_CUSTOM_MARGIN = 8

class CustomTabLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : TabLayout(
    context,
    attrs,
    defStyleAttr
) {

    init {
        val tabStrip = (this.getChildAt(0) as ViewGroup)
        tabStrip.setOnHierarchyChangeListener(object : OnHierarchyChangeListener {
            override fun onChildViewAdded(p0: View?, p1: View?) {
                if (p1 is TabView && p1.layoutParams is MarginLayoutParams) {
                    val params = p1.layoutParams as MarginLayoutParams
                    params.setMargins(YOUR_CUSTOM_MARGIN, 0, YOUR_CUSTOM_MARGIN, 0)
                }
            }

            override fun onChildViewRemoved(p0: View?, p1: View?) {}
        })
    }
}
FJJ
  • 131
  • 5