In a project, we are using the Material Design Components' TabLayout
with a ViewPager
. There are 14 tabs, and we want 7 of those tabs to be visible at a time in the TabLayout
. The tab content is narrow enough that we are sure that 7 will not be too many, and the design team wants a consistent number of tabs showing up regardless of screen width (tabs represent days of the week).
None of the pre-defined tab modes seem to match this:
MODE_FIXED
and MODE_AUTO
control the number of visible tabs... by showing all of themMODE_SCROLLABLE
allows the tabs to scroll... but then we do not have control over the number of visible tabsIs there a way of accomplishing this that does not involve non-maintainable hacks, such as using reflection to tinker with tabPaddingStart
at runtime, or iterating over the tab widgets and adjusting their LayoutParams
?
I have seen this question, but the explanation is lacking — in particular, it is unclear how to use app:tabMaxWidth
for what should be a dynamic value at runtime. Also, that question is about the older Design Support Library, which may differ somewhat with MDC's implementation.
There several ways to show a fixed number of tabs irrespective of screen width that could work, but the desired functionality is really locked down. Most notably, if getTabMinWidth() in TabLayout were not private, an easy solution would be to override that method in a custom TabLayout view.
The following is along that lines of, and maybe exactly, what Eugen Pechanec suggested in a comment above which involves a custom view for the tabs.
First the base layout.
activity_main.xml
tabMinWidth
, tabPaddingEnd
and tabPaddingStart
are all set to 0dp
. tabMinWidth
has a default value that is probably too large for our needs. The padding could be set to other than zero, but I would rather deal with that in the custom views for the tabs.
Nothing really happens with the ViewPager.
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabMinWidth="0dp"
app:tabMode="scrollable"
app:tabPaddingEnd="0dp"
app:tabPaddingStart="0dp" />
</androidx.viewpager.widget.ViewPager>
</androidx.appcompat.widget.LinearLayoutCompat>
custom_tab.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Tab x"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
<!-- If an icon is needed. -->
<!-- <ImageView-->
<!-- android:id="@android:id/icon"-->
<!-- android:layout_width="48dp"-->
<!-- android:layout_height="48dp"-->
<!-- android:scaleType="centerCrop"-->
<!-- android:src="@drawable/ic_launcher_foreground" />-->
</LinearLayout>
MainActivity.kt
Tabs are loaded into the TabLayout one-by-one setting the custom views as we go. The minimum width of the custom views is set to 1/7 of the width of the TabLayout. Setting the minimum width suffices since it is given that the width needed will always be less than or equal to 1/7 of the total width.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val tabLayout = findViewById<TabLayout>(R.id.tabs)
tabLayout.doOnLayout {
val tabWidth = tabLayout.width / 7
for (i in 1..14) {
tabLayout.newTab().run {
setCustomView(R.layout.custom_tab)
customView?.minimumWidth = tabWidth
setText("Tab $i")
tabLayout.addTab(this)
}
}
}
}
}
If custom tabs are used anyway, I think that this is a reasonable solution. However, it is little better (IMO) than iterating over the TabLayout children and setting widths.
Finally, a couple of pictures:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With