【问题标题】:How to add margin between tabs in TabLayout?如何在 TabLayout 中的选项卡之间添加边距?
【发布时间】:2016-08-01 15:01:10
【问题描述】:

有没有办法在 TabLayout 中的选项卡之间添加边距?我尝试过为 Widget.Design.TabLayout 使用自定义样式,但有些属性仅与填充相关,但没有边距。

【问题讨论】:

  • margin 意味着什么.. 选项卡之间的间隙?
  • 似乎添加边距实际上是不可能的,但是您可以做一些技巧来使其工作。可以添加一个虚拟视图或选项卡。

标签: android android-tablayout


【解决方案1】:

好的,伙计们,在花了 2-3 个小时之后,我终于找到了解决方案。

如果您使用的是 TabLayout,则无法通过使用样式等为选项卡添加边距。 (如@Connecting life with Android之前)

但是,您可以通过编写一些 Java 代码来做到这一点。总而言之,您的代码应该与那个相似:

            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();
            }

为了将每个选项卡作为视图,我们必须首先获取包含它们的容器。在这种情况下,TabLayout 使用 SlidingTabStrip 作为选项卡的容器。 SlidingTabStrip 是 TabLayout 的第一个子项:

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

在这个小细节之后,一切都非常简单。

【讨论】:

  • 您的解决方案非常适合我!如果有人想在 dimens.xml 文件中添加一个边距,他可以使用 getResources().getDimensionPixelSize(R.dimen.your_dimens) 而不是像“50”这样的固定像素值。
  • 这行得通,但是当你这样做时,下划线指示器开始出现异常,滑动工作正常,但在标签点击时分页到给定点会破坏它。我正在寻找解决办法。
  • 这对我帮助很大。谢谢。
  • 完美解决方案。
  • @Todor 你的回答很好。您共享的代码 sn-p 没有为最后一个选项卡设置边距。那么你能告诉我如何实现这一目标吗?
【解决方案2】:

这是我在纯 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. -->

包含您的 TabLayout 的布局:

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

在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>

...这就是诀窍所​​在。有效地,根据您的 @dimen/tab_spacing_half 值将您的背景形状包裹在带有“填充”的 item 中。最后……

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...我正在寻找的只是
  • 这应该是公认的答案。无需 java/kotlin “hacking” 即可工作。
  • 这是一个很好的答案并且易于理解。谢谢@ban-geoengineering
【解决方案3】:

这是@Todor Kostov 答案的 Kotlin 版本。

 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()
 }

【讨论】:

    【解决方案4】:

    @Todor Kostov 回答得很好,但是标签的中心被滑走了,因为最后一个标签也有边距。

    所以请使用mTabLayout.getTabCount() - 1 而不仅仅是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();
            }
    

    【讨论】:

      【解决方案5】:

      这是为四个不同的选项卡设置边距的方法。您可以更改 setMargins(v1,v2,v3,v4) 函数值以获得适合您正在使用的选项卡数量的拟合。我希望这有帮助。请注意,tabHost 是 TabHost 托管您正在使用的不同选项卡的对象。

      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);
      
                  }
      

      【讨论】:

        【解决方案6】:

        这里的大多数建议似乎都建议调用 requestLayout() 来更新边距。这将触发一个新的布局传递。我个人在为布局设置动画时也看到了一些问题,其中定义了 TabLayout,导致 requestLayout 被调用了很多次,有时会使选项卡“跳转”

        我认为设置选项卡之间的边距的更好方法是扩展 TabLayout,并在子视图添加到 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?) {}
                })
            }
        }
        

        【讨论】: