我们如何为固定可见数量的可滚动选项卡配置 TabLayout?
How Can We Configure TabLayout For a Fixed Visible Number of Scrollable Tabs?
在一个项目中,我们使用 Material 设计组件的 TabLayout
和 ViewPager
。有 14 个选项卡,我们希望其中 7 个选项卡在 TabLayout
中一次可见。选项卡内容足够窄,我们确信 7 个不会太多,设计团队希望无论屏幕宽度如何,显示的选项卡数量一致(选项卡代表一周中的几天)。
None 的预定义选项卡模式似乎与此匹配:
MODE_FIXED
和 MODE_AUTO
控制可见选项卡的数量...通过显示所有选项卡
MODE_SCROLLABLE
允许选项卡滚动...但是我们无法控制可见选项卡的数量
有没有一种不涉及不可维护的 hack 的方法来实现这一点,例如在运行时使用反射来修补 tabPaddingStart
,或者遍历选项卡小部件并调整它们的 LayoutParams
?
我看过 ,但缺乏解释 — 特别是,不清楚如何使用 app:tabMaxWidth
作为运行时的动态值。此外,该问题与旧的设计支持库有关,它可能与 MDC 的实现有所不同。
我不得不做同样的事情,我特别注意调查了这个问题。剧透:我失败了 - 目前您想要实现的目标无法通过库存 TabLayout
和清洁 Android 方式实现。
虽然有黑客)
最合适的方法是使用允许这样做的库 - 例如 this
虽然你提到了它并且不想这样做 - 迭代 TabLayout
children 改变他们的 LayoutParams
是下一个适当的方法它。该方法利用了public提供的Android方法,不需要使用一些不太好的技术。说起来...
*反思开始
更改 tabPaddingStart
或 requestedTabMinWidth
以及 requestedTabMaxWidth
。这是坏的。我什至不会开始为什么。这是此列表中最不充分的一种方式。它仍然是一种修复它的方法。
这是干净的 Android 方式和 TabLayout
参数 app:tabMaxWidth
和 app:tabMinWidth
以及反射的组合。在这种情况下,反射有点不同——它几乎是正常的(尽可能多的反射)。我建议在 integers.xml
中创建一个名为 tabWidth
的 integer
android 资源(我相信你已经知道我要去哪里了),然后在应用程序开始时替换它通过反射将 screenWidth
的值除以 7(或更少,具体取决于您的填充)。
*反射完成
- 还有另一种方法,它 Android 干净但仍然不够。您可以使用
ViewPagers
and/or 片段的组合。最简单的是两个 ViewPager
和两个 TabLayout
的 ViewPager
,每个都有 7 页和 7 个标签。不过,您可能需要调整手势处理程序。并且可以有更多的组合。
我不会包括你在 RecyclerView
和 SnapHelper
的帮助下自己编写 TabLayout
的方法,因为你总是可以选择自己在 Android...浪费大量时间。
我知道这不是答案。此外,它是如何不做 Android 中的事情的列表,但有时我们需要选择黑暗面...
希望对您有所帮助。
有几种方法可以显示固定数量的选项卡,而不管屏幕宽度如何,但所需的功能确实被锁定了。最值得注意的是,如果 getTabMinWidth() in TabLayout 不是私有的,一个简单的解决方案是在自定义 中覆盖该方法TabLayout 视图。
以下是 Eugen Pechanec 在上面的评论中所建议的,也许正是,其中涉及选项卡的自定义视图。
首先是基本布局。
activity_main.xml
tabMinWidth
、tabPaddingEnd
和tabPaddingStart
都设置为0dp
。 tabMinWidth
有一个默认值,可能对我们的需要来说太大了。填充可以设置为非零,但我宁愿在选项卡的自定义视图中处理它。
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)。
最后,来几张图:
在一个项目中,我们使用 Material 设计组件的 TabLayout
和 ViewPager
。有 14 个选项卡,我们希望其中 7 个选项卡在 TabLayout
中一次可见。选项卡内容足够窄,我们确信 7 个不会太多,设计团队希望无论屏幕宽度如何,显示的选项卡数量一致(选项卡代表一周中的几天)。
None 的预定义选项卡模式似乎与此匹配:
MODE_FIXED
和MODE_AUTO
控制可见选项卡的数量...通过显示所有选项卡MODE_SCROLLABLE
允许选项卡滚动...但是我们无法控制可见选项卡的数量
有没有一种不涉及不可维护的 hack 的方法来实现这一点,例如在运行时使用反射来修补 tabPaddingStart
,或者遍历选项卡小部件并调整它们的 LayoutParams
?
我看过 app:tabMaxWidth
作为运行时的动态值。此外,该问题与旧的设计支持库有关,它可能与 MDC 的实现有所不同。
我不得不做同样的事情,我特别注意调查了这个问题。剧透:我失败了 - 目前您想要实现的目标无法通过库存 TabLayout
和清洁 Android 方式实现。
虽然有黑客)
最合适的方法是使用允许这样做的库 - 例如 this
虽然你提到了它并且不想这样做 - 迭代
TabLayout
children 改变他们的LayoutParams
是下一个适当的方法它。该方法利用了public提供的Android方法,不需要使用一些不太好的技术。说起来...
*反思开始
更改
tabPaddingStart
或requestedTabMinWidth
以及requestedTabMaxWidth
。这是坏的。我什至不会开始为什么。这是此列表中最不充分的一种方式。它仍然是一种修复它的方法。这是干净的 Android 方式和
TabLayout
参数app:tabMaxWidth
和app:tabMinWidth
以及反射的组合。在这种情况下,反射有点不同——它几乎是正常的(尽可能多的反射)。我建议在integers.xml
中创建一个名为tabWidth
的integer
android 资源(我相信你已经知道我要去哪里了),然后在应用程序开始时替换它通过反射将screenWidth
的值除以 7(或更少,具体取决于您的填充)。
*反射完成
- 还有另一种方法,它 Android 干净但仍然不够。您可以使用
ViewPagers
and/or 片段的组合。最简单的是两个ViewPager
和两个TabLayout
的ViewPager
,每个都有 7 页和 7 个标签。不过,您可能需要调整手势处理程序。并且可以有更多的组合。
我不会包括你在 RecyclerView
和 SnapHelper
的帮助下自己编写 TabLayout
的方法,因为你总是可以选择自己在 Android...浪费大量时间。
我知道这不是答案。此外,它是如何不做 Android 中的事情的列表,但有时我们需要选择黑暗面...
希望对您有所帮助。
有几种方法可以显示固定数量的选项卡,而不管屏幕宽度如何,但所需的功能确实被锁定了。最值得注意的是,如果 getTabMinWidth() in TabLayout 不是私有的,一个简单的解决方案是在自定义 中覆盖该方法TabLayout 视图。
以下是 Eugen Pechanec 在上面的评论中所建议的,也许正是,其中涉及选项卡的自定义视图。
首先是基本布局。
activity_main.xml
tabMinWidth
、tabPaddingEnd
和tabPaddingStart
都设置为0dp
。 tabMinWidth
有一个默认值,可能对我们的需要来说太大了。填充可以设置为非零,但我宁愿在选项卡的自定义视图中处理它。
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)。
最后,来几张图: