简体   繁体   English

Android 选项卡布局:根据选项卡标题换行选项卡指示器宽度

[英]Android Tab layout: Wrap tab indicator width with respect to tab title

Is there any way to wrap tab indicator width with respect to tab title?有什么方法可以根据标签标题包装标签指示器宽度?

当前的

必需的

Yes , it's possible to wrap tab indicator as title setting padding to 0是的,可以将标签指示符包装为标题设置填充为 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;
    }
}

结果

EDIT : As of com.android.support:design:28.0.0 , you can adjust the indicator as label easily now setting:编辑:从com.android.support:design:28.0.0 ,您可以轻松地将指标调整为标签,现在设置:

app:tabIndicatorFullWidth="false"

EDIT July 2019 : Use the androidX dependency com.google.android.material:material:xxx编辑 2019 年 7 月:使用 androidX 依赖项com.google.android.material:material:xxx

As of support library 28 you can do the following:从支持库 28 开始,您可以执行以下操作:

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.这将设置一个自定义 drawable 作为指标。

example of custom drawable:自定义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>

在此处输入图片说明

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.对于添加的点,您可以检查每个标题的文本大小。

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'

一个简单的解决方案是将此属性添加到您的 tabLayout

app:tabIndicatorFullWidth="false"

Short answer is "no".简短的回答是“不”。 Here is explanation.这是解释。

There is private class SlidingTabStrip within TabLayout that draws indicator SlidingTabStrip有一个私有类 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);
        }
    }

I believe mIndicatorLeft and mIndicatorRight is what you need.我相信 mIndicatorLeft 和 mIndicatorRight 是您所需要的。 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.最糟糕的是 TabLayout 中的 SlidingTabStrip 字段是私有的和最终的。

private final SlidingTabStrip mTabStrip;

I don't see how it's possible to achieve what you need without creating totally new TabLayout.我不知道如何在不创建全新的 TabLayout 的情况下实现您的需求。

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.我确定需要的 SDK 版本,但您可以直接在 XML 文件中完成,使用 app 命名空间添加 padding 属性。

Just make your TabLayout looks like this:只需让您的 TabLayout 看起来像这样:

        <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 :)你也可以为你的父布局移动xmlns:app="http://schemas.android.com/apk/res-auto" ,但它是你的选择:)

Thanks leon for his answer, that lead me to achieve that by XML.感谢 leon 的回答,这让我通过 XML 实现了这一目标。

I tried 2 hours for various solutions but none of those have a perfect effect.我尝试了 2 个小时的各种解决方案,但没有一个有完美的效果。
The problem lies on the tabView's padding - even if I set its padding to all 0,问题在于 tabView 的填充——即使我将其填充设置为全 0,
Different tabs still have different padding and therefore the text inside its TextView varies in size.不同的选项卡仍然具有不同的填充,因此其TextView的文本大小不同。

Then I found this library which perfectly solved it.然后我找到了这个库,它完美地解决了它。
https://github.com/H07000223/FlycoTabLayout https://github.com/H07000223/FlycoTabLayout
As @Stas Melnychenko said, we probably can't do it unless we re-write a TabLayout .正如@Stas Melnychenko 所说,除非我们重新编写TabLayout ,否则我们可能无法做到。 And this library does re-write a TabLayout ...这个库确实重写了一个TabLayout ...

You can do it using styles.您可以使用样式来完成。

In the layout xml:在布局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:在styles.xml中:

<style name="AppTabLayout" parent="Widget.Design.TabLayout">
    <item name="android:layout_marginLeft">40dp</item>
    <item name="android:layout_marginRight">40dp</item>
</style>

There's a attribute called tabIndicatorFullWidth inside android.support.design.widget.TabLayout在 android.support.design.widget.TabLayout 中有一个名为 tabIndicatorFullWidth 的属性

<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" />

You can do this:你可以这样做:

layout/layout_demo.xml布局/layout_demo.xml

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

res/tab_indicator.xml 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>

We can easily set the width of the Tab indicator with respect to its Tab Title.我们可以轻松地设置 Tab 指示器相对于其 Tab Title 的宽度。 Because of the com.android.support:design:28.0.0, we can easily adjust it using below code in xml file:由于 com.android.support:design:28.0.0,我们可以使用以下 xml 文件中的代码轻松调整它:

app:tabIndicatorFullWidth="false"应用程序:tabIndicatorFullWidth="false"

For me app:tabIndicatorFullWidth="false" works perfectly.对我来说app:tabIndicatorFullWidth="false"工作完美。 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>

You just need to add app:tabMode="scrollable" .您只需要添加app:tabMode="scrollable" It automatically resizes它会自动调整大小

Simply add spaces or spaces on either side of the text of the tab:只需在选项卡文本的任一侧添加空格或空格:

    tabLayout.addTab(tabLayout.newTab().setText("        Login        "));
    tabLayout.addTab(tabLayout.newTab().setText("        Sign Up        "));

and don't forget to set the attribute:并且不要忘记设置属性:

        app:tabIndicatorFullWidth="false"

in active.在活跃。

here is the result: enter image description here这是结果:在此处输入图像描述

This is my code:这是我的代码:

+) In.xml +) In.xml

<com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabIndicatorColor="#D741C9EC"
            app:tabIndicatorHeight="3dp"
            app:tabSelectedTextColor="#D741C9EC"
            app:tabIndicatorFullWidth="false"

            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.0"
            tools:ignore="MissingConstraints"></com.google.android.material.tabs.TabLayout>

+) In adapter: LoginAdapter.java +) 在适配器中:LoginAdapter.java

public class LoginAdapter extends FragmentPagerAdapter {
    Context context;
    int tab;

    public LoginAdapter(@NonNull FragmentManager fm, Context context, int tab) {
        super(fm);
        this.context = context;
        this.tab = tab;
    }

    public LoginAdapter(@NonNull FragmentManager fm, int behavior, Context context, int tab) {
        super(fm, behavior);
        this.context = context;
        this.tab = tab;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        switch (position){
            case 0:
                SignInFragment signInFragment = new SignInFragment();
                return signInFragment;
            case 1:
                SignUpFragment signUpFragment = new SignUpFragment();
                return signUpFragment;
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return tab;
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return "                   Login                   ";
            case 1:
                return "                   Sign Up                   ";
        }
        return null;
    }
}

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM