我们如何为固定可见数量的可滚动选项卡配置 TabLayout?

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

在一个项目中,我们使用 Material 设计组件的 TabLayoutViewPager。有 14 个选项卡,我们希望其中 7 个选项卡在 TabLayout 中一次可见。选项卡内容足够窄,我们确信 7 个不会太多,设计团队希望无论屏幕宽度如何,显示的选项卡数量一致(选项卡代表一周中的几天)。

None 的预定义选项卡模式似乎与此匹配:

有没有一种不涉及不可维护的 hack 的方法来实现这一点,例如在运行时使用反射来修补 tabPaddingStart,或者遍历选项卡小部件并调整它们的 LayoutParams

我看过 ,但缺乏解释 — 特别是,不清楚如何使用 app:tabMaxWidth 作为运行时的动态值。此外,该问题与旧的设计支持库有关,它可能与 MDC 的实现有所不同。

我不得不做同样的事情,我特别注意调查了这个问题。剧透:我失败了​​ - 目前您想要实现的目标无法通过库存 TabLayout 和清洁 Android 方式实现。

虽然有黑客)

  • 最合适的方法是使用允许这样做的库 - 例如 this

  • 虽然你提到了它并且不想这样做 - 迭代 TabLayout children 改变他们的 LayoutParams 是下一个适当的方法它。该方法利用了public提供的Android方法,不需要使用一些不太好的技术。说起来...

*反思开始

  • 更改 tabPaddingStartrequestedTabMinWidth 以及 requestedTabMaxWidth。这是坏的。我什至不会开始为什么。这是此列表中最不充分的一种方式。它仍然是一种修复它的方法。

  • 这是干净的 Android 方式和 TabLayout 参数 app:tabMaxWidthapp:tabMinWidth 以及反射的组合。在这种情况下,反射有点不同——它几乎是正常的(尽可能多的反射)。我建议在 integers.xml 中创建一个名为 tabWidthinteger android 资源(我相信你已经知道我要去哪里了),然后在应用程序开始时替换它通过反射将 screenWidth 的值除以 7(或更少,具体取决于您的填充)。

*反射完成

  • 还有另一种方法,它 Android 干净但仍然不够。您可以使用 ViewPagers and/or 片段的组合。最简单的是两个 ViewPager 和两个 TabLayoutViewPager,每个都有 7 页和 7 个标签。不过,您可能需要调整手势处理程序。并且可以有更多的组合。

我不会包括你在 RecyclerViewSnapHelper 的帮助下自己编写 TabLayout 的方法,因为你总是可以选择自己在 Android...浪费大量时间。

我知道这不是答案。此外,它是如何不做 Android 中的事情的列表,但有时我们需要选择黑暗面...

希望对您有所帮助。

有几种方法可以显示固定数量的选项卡,而不管屏幕宽度如何,但所需的功能确实被锁定了。最值得注意的是,如果 getTabMinWidth() in TabLayout 不是私有的,一个简单的解决方案是在自定义 中覆盖该方法TabLayout 视图。

以下是 Eugen Pechanec 在上面的评论中所建议的,也许正是,其中涉及选项卡的自定义视图。

首先是基本布局。

activity_main.xml

tabMinWidthtabPaddingEndtabPaddingStart都设置为0dptabMinWidth 有一个默认值,可能对我们的需要来说太大了。填充可以设置为非零,但我宁愿在选项卡的自定义视图中处理它。

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 被加载到 TabLayout 一个接一个地设置自定义视图。自定义视图的最小宽度设置为 TabLayout 宽度的 1/7。设置最小宽度就足够了,因为需要的宽度总是小于或等于总宽度的 1/7。

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

    }
}

如果仍然使用自定义选项卡,我认为这是一个合理的解决方案。但是,它比遍历 TabLayout 子级并设置宽度好一点(IMO)。

最后,来几张图: