42、Material Design之TabLayout:Tab模式、自定义Tab、与ViewPager2结合
TabLayout,说白了就是Android里做顶部导航标签的标配。我最早接触它是在Material Design刚推出那会儿,那时候大家还在用第三方的PagerSlidingTabStrip。后来Google官方出了TabLayout,我就彻底弃坑了。嗯,今天咱们就把它聊透。
一、TabLayout的三种Tab模式
TabLayout支持三种模式,我习惯用MODE_FIXED,但具体用哪个得看场景。
| 模式 | 常量 | 适用场景 |
|---|---|---|
| 固定模式 | MODE_FIXED | Tab数量少(≤4个),所有Tab等宽显示 |
| 可滚动模式 | MODE_SCROLLABLE | Tab数量多(≥5个),左右滑动查看 |
| 自动模式 | MODE_AUTO | 根据宽度自动选择固定或滚动 |
我的经验:MODE_AUTO看起来智能,但实际效果有时会尴尬。比如屏幕旋转时模式突然切换,用户体验并不好。我个人建议:明确场景,手动指定模式。
代码里设置很简单:
TabLayout tabLayout = findViewById(R.id.tabLayout);
tabLayout.setTabMode(TabLayout.MODE_FIXED); // 或 MODE_SCROLLABLE
二、自定义Tab:别再用默认样式了
默认的Tab样式太素了。我在项目中做过一个电商App,产品经理要求Tab里带小红点和图标。默认的TabLayout根本满足不了。怎么办?自定义Tab视图。
核心思路:通过setCustomView()给每个Tab注入你写的布局。
先写一个自定义Tab布局 tab_custom.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/tab_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_home" />
<TextView
android:id="@+id/tab_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="12sp" />
<!-- 小红点 -->
<View
android:id="@+id/tab_badge"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginTop="2dp"
android:background="@drawable/bg_red_dot"
android:visibility="gone" />
</LinearLayout>
然后在代码里绑定:
for (int i = 0; i < titles.length; i++) {
TabLayout.Tab tab = tabLayout.newTab();
View customView = getLayoutInflater().inflate(R.layout.tab_custom, null);
TextView tvTitle = customView.findViewById(R.id.tab_title);
ImageView ivIcon = customView.findViewById(R.id.tab_icon);
tvTitle.setText(titles[i]);
ivIcon.setImageResource(icons[i]);
tab.setCustomView(customView);
tabLayout.addTab(tab);
}
小技巧:自定义Tab视图的高度不要写死,用wrap_content。否则不同屏幕密度下容易变形。我曾经踩过这个坑,在平板上一看,图标都挤变形了。
三、TabLayout + ViewPager2:黄金搭档
ViewPager2是ViewPager的升级版,支持RTL、垂直滑动,还解决了嵌套滑动冲突。我建议新项目直接用ViewPager2,别再用老版了。
两者结合的核心是 TabLayoutMediator。注意,不是以前的 setupWithViewPager(),那个方法在ViewPager2里已经废弃了。
ViewPager2 viewPager2 = findViewById(R.id.viewPager2);
TabLayout tabLayout = findViewById(R.id.tabLayout);
// 先给ViewPager2设置Adapter
MyPagerAdapter adapter = new MyPagerAdapter(this);
viewPager2.setAdapter(adapter);
// 再用TabLayoutMediator绑定
new TabLayoutMediator(tabLayout, viewPager2,
(tab, position) -> {
tab.setText(titles[position]);
tab.setIcon(icons[position]);
}
).attach();
这里有个细节:TabLayoutMediator的第三个参数是一个回调,你在里面配置每个Tab的样式。如果你用了自定义视图,就在这里调用tab.setCustomView()。
注意:一定要先给ViewPager2设置Adapter,再调用attach()。顺序反了会报空指针。我曾经调试了半小时才发现是顺序问题。
四、避坑指南
- Tab选中状态不更新:检查是否在
TabLayoutMediator的回调里正确设置了选中/未选中的样式。我习惯在回调里根据position判断当前Tab是否选中。 - 自定义Tab点击区域太小:给自定义布局的根布局设置
android:clickable="true",否则点击事件可能被TabLayout拦截。 - ViewPager2滑动卡顿:检查Fragment是否在
onCreateView()里做了耗时操作。ViewPager2默认预加载一个相邻页面,如果Fragment初始化太重,就会卡。 - Tab指示器宽度不对:自定义Tab时,指示器宽度默认是Tab的宽度。如果你想让指示器只包裹文字,可以设置
app:tabIndicatorFullWidth="false"。
五、知识体系图
下面这张图梳理了TabLayout的核心知识点和关联关系,方便你快速回顾:
六、总结
TabLayout + ViewPager2这套组合,是Android里做页面导航最常用的方案。记住三点:
- Tab模式根据Tab数量选,别偷懒用AUTO
- 自定义Tab视图时注意布局层级,别嵌套太深
- TabLayoutMediator是绑定的正确姿势,别忘了先设Adapter
我在项目里用这套方案做过十几个页面,从简单的设置页到复杂的电商首页,基本都能覆盖。你只要把上面这些点吃透,遇到类似需求就能信手拈来。
公众号:蓝海资料掘金营,微信deep3321