material-components-android
material-components-android copied to clipboard
[TabLayout] Support gravity_fill with "auto" mode
Description: Setting the tabMode
to auto
is the same as setting the tabMode
to scrollable
. Even if there are only 2 items, they will never act like fixed
tabs.
Expected behavior: Expected to act as in the documentation
Auto-sizing tabs behave like MODE_FIXED with GRAVITY_CENTER while the tabs fit within the TabLayout's content width. Fixed tabs have equal width, based on the widest tab label. Once the tabs outgrow the view's width, auto-sizing tabs behave like MODE_SCROLLABLE, allowing for a dynamic number of tabs without requiring additional layout logic.
Source code:
By adding app:tabMode="auto"
to the tab_layout
view in component_tabs.xml
and running the Material Theme Builder, you can see that the tabs no longer take up the full width of the parent. This occurs regardless of whether the parent is a LinearLayout or ConstraintLayout.
In updateTabViewLayoutParams
in TabLayout
, it sets the tabView
layout params' width to WRAP_CONTENT
if the mode is not MODE_FIXED
, not sure if that's the issue.
Android API version: Seen on API 29.
Material Library version: Seen on alpha07, alpha09, and alpha10.
Device: Seen on Pixel 3 emulator.
The documentation you cite matches the current behavior:
Fixed tabs have equal width, based on the widest tab label
That's how gravity_center
behaves vs gravity_fill
which seems to be what you want. Let me know if you want to update this to a feature request or you could send us a pull request.
Ahhhh thank you @ymarian , so if the tabMode is set to auto, then setting gravity_fill
gets ignored? Thanks for clarifying!
I was hoping it could work with gravity_fill
- so it would automatically use the half-and-half setup for two tabs, third-third-third for three, and then if e.g. 6 tabs are added, it would go to the scrolling version. I will remove the bug
label and read the docs more carefully :)
Edit: looks like I can't remove the bug label 😅
Append the names of the tab in a list. So that you can do something like that:
(getTabNameIds().size() < 3) ? TabLayout.MODE_FIXED : TabLayout.MODE_SCROLLABLE;
The source of this issue:
private void updateTabViewLayoutParams(@NonNull LinearLayout.LayoutParams lp) {
if (mode == MODE_FIXED && tabGravity == GRAVITY_FILL) {
lp.width = 0;
lp.weight = 1;
} else {
lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
lp.weight = 0;
}
}
Shouldn't it be as simple as removing the mode check? If the user requested gravity=fill, then weight=1 should be applied regardless of mode.
The source of this issue:
private void updateTabViewLayoutParams(@NonNull LinearLayout.LayoutParams lp) { if (mode == MODE_FIXED && tabGravity == GRAVITY_FILL) { lp.width = 0; lp.weight = 1; } else { lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; lp.weight = 0; } }
Shouldn't it be as simple as removing the mode check? If the user requested gravity=fill, then weight=1 should be applied regardless of mode.
I think so too. The introduction of a boolean type parameter, such as tabModeAutoFillWidth, would likely be for the purpose of configuring a user interface element in a software application. For instance, if you're referring to tabs in a tab bar or a navigation component, this parameter could control whether the tabs automatically resize to fill the available width.
Here’s how such a parameter might be used in different contexts:
private void updateTabViewLayoutParams(@NonNull LinearLayout.LayoutParams lp) {
if (mode == MODE_FIXED && tabGravity == GRAVITY_FILL) {
lp.width = 0;
lp.weight = 1;
} else if (tabModeAutoFillWidth && mode == MODE_AUTO && tabGravity == GRAVITY_FILL) {
lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
lp.weight = 1;
} else {
lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
lp.weight = 0;
}
}