Android Tab布局:根据选项卡标题自动调整选项卡指示器宽度

45

有没有办法根据选项卡标题来调整选项卡指示器的宽度?

当前

所需

16个回答

54

是的,将标签指示器作为标题进行包装并将内边距设置为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;
    }
}

结果

编辑: 自com.android.support:design:28.0.0起,您现在可以轻松地将指示器调整为标签,设置如下:

app:tabIndicatorFullWidth="false"

编辑于2019年7月: 使用material依赖项 com.google.android.material:material:x.x.x


谢谢 @leon, 使用可滚动的TabLayout(tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE))对我很有效。 - Hermandroid
1
这个有效。请确保您在XML中设置了以下内容:app:tabPadding="0dp" - dazza5000
对我来说运行良好。 - malli
5
外部边距和内部边距的值是多少?我已经给出了0,0,仅适用于选项卡模式为可滚动(tabMode=scrollable),对于固定选项卡模式(tabMode=fixed)无效。 - Suresh Gogu
2
它对我不起作用..我需要解决方案。我正在使用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"/>我正在使用两个选项卡,但仍然填充了一半的宽度....我不知道为什么会这样。你能给出一些解决方案吗? - prakash
显示剩余2条评论

33

从支持库28开始,您可以执行以下操作:

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

您可以设置所需的填充来影响选项卡指示器。

此外,您现在可以这样做:

app:tabIndicator="@drawable/tab_indicator"

这将设置自定义的可绘制对象作为指示器。

自定义可绘制对象示例:

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

2
如何为选项卡指示器设置固定宽度?通过使用属性app:tabIndicatorFullWidth="false",选项卡指示器的宽度会根据选项卡文本的宽度而改变。我想要一个固定宽度的选项卡指示器,我该怎么做? - K Pradeep Kumar Reddy

12

输入图像描述

如果您不需要条纹比文本小,则应该可以使用以下方法:

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

如果你愿意的话,你可以检查每个标题的文本大小以获得额外加分。


@Vishvadave,感谢您的反馈,您能提供有关您的设置的任何详细信息吗? - Erik B
对我有用!非常感谢。 - AdamHurwitz
1
嘿,谢谢,这个工作得很好,但是如果我想让指示器的宽度小于文本怎么办? - Kaustubh Bhagwat
很遗憾,这种方法不可能实现。如果你找到了一种方法,请告诉我。 - Erik B

7
一个简单的解决方案是:
tabLayout.setTabIndicatorFullWidth(false);

但是它可以与最新的依赖项一起使用,例如:

implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:design:28.0.0'

如何为选项卡指示器设置固定宽度?通过使用属性app:tabIndicatorFullWidth="false",选项卡指示器的宽度会根据选项卡文本的宽度而改变。我想要一个固定宽度的选项卡指示器,我该怎么做? - K Pradeep Kumar Reddy
嗨,@KPradeepKumarReddy,您可以尝试以下属性: app:tabMaxWidth="0dp" app:tabGravity="fill" app:tabMode="fixed" 注意:我没有执行,如果上述解决方案不起作用,请提前谅解。 - sudhakara k s

5

你可以这样做:

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>

4

简短的回答是“不行”。以下是解释。

TabLayout 中有一个名为 SlidingTabStrip 的私有类,用于绘制指示器。

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

我相信你需要的是mIndicatorLeft和mIndicatorRight。这些字段在同一个类中设置:

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

其中左右参数在下一个方法中计算:

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

最糟糕的是,TabLayout中的SlidingTabStrip字段是私有和final的。

private final SlidingTabStrip mTabStrip;

我不认为在不创建全新的TabLayout的情况下,你能实现你所需的功能。


当我将gravity设置为fill,并将左右padding设为12时,指示器停在第二个选项卡标题的末尾,没有扩展到其他地方。如果可能的话,是否有办法将其他边缘包裹起来?@StasMelnychenko - Narayan C.R
当我将gravity设置为fill并将左右padding设置为12时,您指的是哪些属性?TabLayout吗? - Sviatoslav Melnychenko
在 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"/>' - Narayan C.R
更改TabLayout的padding意味着你剪掉了包含指示器的整个条纹。 - Sviatoslav Melnychenko

4

一种简单的解决方案是在您的tabLayout中添加此属性。

app:tabIndicatorFullWidth="false"

1
如何为选项卡指示器设置固定宽度?通过使用属性app:tabIndicatorFullWidth="false",选项卡指示器的宽度会根据选项卡文本的宽度而改变。我想要一个固定宽度的选项卡指示器,该怎么做? - K Pradeep Kumar Reddy

1
你可以使用样式来实现。
在布局XML中:
<android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        style="@style/AppTabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

在 styles.xml 文件中:
<style name="AppTabLayout" parent="Widget.Design.TabLayout">
    <item name="android:layout_marginLeft">40dp</item>
    <item name="android:layout_marginRight">40dp</item>
</style>

0

您只需要添加app:tabMode="scrollable"。它会自动调整大小


0
我们可以轻松地根据选项卡标题设置选项卡指示器的宽度。由于 com.android.support:design:28.0.0 的存在,我们可以在 xml 文件中使用以下代码轻松调整它:

app:tabIndicatorFullWidth="false"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接