How Can We Configure TabLayout For a Fixed Visible Number of Scrollable Tabs?

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:

Portrait Orientation

Landscape Orientation