当前位置: 首页 > news >正文

【Android】用 ViewPager2 + Fragment + TabLayout 实现标签页切换

【Android】用 ViewPager2 + Fragment + TabLayout 实现标签页切换

一、引入:什么是 ViewPager2 ?

在我们日常使用的应用中,常常会遇到可以左右滑动切换内容的界面,比如知乎首页的推荐/热榜/关注标签页、微博的频道分类、相册中的多图浏览,甚至首次打开 App 时的欢迎引导页。这类界面背后的滑动切换机制,通常就是通过 Android 提供的 ViewPager 或其升级版 ViewPager2 实现的。ViewPager2 是 Android 提供的现代化分页滑动组件,支持横向与纵向滑动,具备高效的页面管理能力,是实现多页切换、Tab 联动与内容滑动浏览等功能的首选方案。

:ViewPager2 已全面替代 ViewPager,是官方推荐的新项目默认使用的分页滑动组件。

概述:ViewPager2 是基于 RecyclerView 实现的页面切换控件,继承了其高效的视图复用机制和灵活布局能力,支持水平与垂直滑动,通过设置 offscreenPageLimit 控制缓存策略,提供更高性能和更低内存开销。使用 FragmentStateAdapter 或 RecyclerView.Adapter 作为适配器,适配灵活,支持页面生命周期自动管理,同时还支持 PageTransformer 实现炫酷的切换动画,是替代旧版 ViewPager 的推荐方案。

二、ViewPager2 的基础使用

使用前先添加依赖 (build.gradle):

dependencies {// ...implementation 'androidx.viewpager2:viewpager2:1.1.0'implementation 'com.google.android.material:material:1.12.0'
}

注: 检查并使用最新稳定版本号(ViewPager2|Google 的 Maven 存储库)

1. 在布局文件 (activity_main.xml)中添加 ViewPager2
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.viewpager2.widget.ViewPager2android:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>
2. 制作一个 Fragment
2.1 创建一个布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#19CFBE"><TextViewandroid:id="@+id/textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="泥嚎"android:layout_gravity="center"android:textSize="45sp"/></FrameLayout>
2.2 创建一个 Fragment 类绑定这个布局
public class HomeFragment extends Fragment {private FragmentBinding binding;@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {binding = FragmentBinding.inflate(inflater, container, false);return binding.getRoot();}public void onDestroyView() {super.onDestroyView();binding = null;}
}
3. 创建适配器 (MyAdapter.java)
public class MyAdapter extends FragmentStateAdapter {private final List<Fragment> fragmentList;public MyAdapter(@NonNull FragmentActivity fragmentActivity, List<Fragment> fragmentList) {super(fragmentActivity);this.fragmentList = fragmentList;}@NonNull@Overridepublic Fragment createFragment(int position) {// 用于返回指定的fragment界面return fragmentList.get(position);}@Overridepublic int getItemCount() {// 用于加载容器大小return fragmentList != null ? fragmentList.size() : 0;}
}

自定义 Adapter 并继承 FragmentStateAdapter

ViewPager2 的两种适配器类型

RecyclerView.Adapter

  • 这是一个基础类,需要你自己创建 ViewHolder、绑定 View。
  • 适合每个页面是简单的布局 View,不需要 Fragment。
  • 可以直接继承 RecyclerView.Adapter<MyViewHolder> 来实现。
  • 优点:轻量,适合纯视图页面,不用管理 Fragment 复杂生命周期。
  • 缺点:没有 Fragment 的独立生命周期,不能使用 Fragment 特性。

FragmentStateAdapter

  • 继承自 RecyclerView.Adapter,但专门为 ViewPager2 管理 Fragment 做了封装。
  • 内部会自动帮你管理 Fragment 的添加、显示、销毁和状态保存。
  • 适合每页是 Fragment 的场景,比如每页一个独立的界面。
  • 需要实现 createFragment(int position) 来返回对应位置的 Fragment 实例。
4. 在活动中使用
        List<Fragment> fragmentList = new ArrayList<>();fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());fragmentList.add(new HomeFragment());// 设置适配器MyAdapter adapter = new MyAdapter(this, fragmentList);binding.viewPager.setAdapter(adapter);

三、ViewPager2 + Fragment + TabLayout

TabLayout 是 Android 中用于实现选项卡导航的控件,常与 ViewPager2 搭配使用,它可以在界面顶部展示一排可切换的标签页,每个标签通常对应一个页面内容(如 Fragment),用户点击或滑动即可在各页面之间切换。

1. 在布局文件 (activity_main.xml)中添加 ViewPager2 和 TabLayout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><com.google.android.material.tabs.TabLayoutandroid:id="@+id/tab_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/transparent"android:elevation="0dp"app:tabMode="scrollable"app:tabIndicatorColor="@color/pink"/><androidx.viewpager2.widget.ViewPager2android:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/></LinearLayout>
XML 属性方法取值说明
app:tabModesetTabMode()fixed scrollable标签显示模式:固定宽度/可滚动
app:tabGravitysetTabGravity()center fill标签对齐方式:居中/填充
app:tabContentStartsetPadding()尺寸值 (如 16dp)第一个标签的起始边距
app:tabInlineLabelsetInlineLabel()true false图标与文本是否同行显示
app:tabIndicatorColorsetSelectedTabIndicatorColor()颜色值指示器颜色
app:tabIndicatorHeightsetSelectedTabIndicatorHeight()尺寸值指示器高度
app:tabIndicatorFullWidthsetTabIndicatorFullWidth()true false指示器是否与标签等宽
app:tabIndicatorsetSelectedTabIndicator()Drawable 资源自定义指示器 Drawable
app:tabIndicatorGravitysetTabIndicatorGravity()bottom top center stretch指示器位置:底部/顶部/居中/拉伸
app:tabIndicatorAnimationDuration毫秒值 (如 300)指示器动画时长
app:tabIndicatorAnimationModelinear elastic指示器动画模式:线性/弹性
app:tabTextAppearancesetTabTextAppearance()样式资源 (如 @style/MyTab)文本外观样式
app:tabTextColorsetTabTextColors()ColorStateList文本颜色(支持选择器)
app:tabIconTintsetTabIconTint()ColorStateList图标着色(支持选择器)
app:tabBackgroundsetTabBackground()Drawable 资源标签背景(支持选择器)
app:tabRippleColorsetTabRippleColor()ColorStateList点击波纹效果颜色
app:tabMinWidthsetTabMinWidth()尺寸值标签最小宽度
app:tabMaxWidthsetTabMaxWidth()尺寸值标签最大宽度
app:tabPaddingStartsetTabPadding()尺寸值标签起始内边距

2. 使用 TabLayoutMediator 将两者关联

new TabLayoutMediator(binding.tabLayout, binding.viewPager,new TabLayoutMediator.TabConfigurationStrategy() {@Overridepublic void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {//自定义TabViewTextView tabView = new TextView(MainActivity.this);tabView.setText("Fragment " + position);//将tabbView绑定到tabtab.setCustomView(tabView);}}).attach();

创建一个 TabLayoutMediator 实例,传入三个参数:

  • binding.tabLayout:要联动的 TabLayout。
  • binding.viewPager:要联动的 ViewPager2。
  • TabConfigurationStrategy:回调接口,用于配置每一个 Tab(比如设置标题、图标等)。

onConfigureTab(...)

每个 Tab 初始化时,这个方法都会被调用一次,对应 ViewPager2 的每一页。

tab.setCustomView(tabView);

设置 Tab 的视图,可以完全控制 Tab 的外观,而不再受默认样式限制。也可以用自己写的 layout 文件,比如:

View customTab = LayoutInflater.from(context).inflate(R.layout.my_tab_layout, null);
tab.setCustomView(customTab);

3. ViewPager2 的页面切换监听

binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {@Overridepublic void onPageSelected(int position) {super.onPageSelected(position);// 这里可以处理页面切换时的逻辑}});

这是给 ViewPager2 注册一个 页面变更回调监听器(PageChangeCallback),当用户手动或编程切换页面时,可以在对应的方法中执行你想要的逻辑。

registerOnPageChangeCallback(...)

向 ViewPager2 注册一个页面变化的监听器,类型是 ViewPager2.OnPageChangeCallback,可以监听 当前页面的状态变化(比如选中、滑动、滚动状态变化等)

new ViewPager2.OnPageChangeCallback()

这是创建一个监听器对象,并重写其回调方法。该监听器包含以下常用方法:

  • onPageSelected(int position)
    • 调用时机:页面切换完成后(即滑动结束,页面完全展示出来时)。
    • 适合场景
      • 更新 TabLayout 高亮
      • 改变底部按钮状态
      • 动态加载数据(懒加载)
  • onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    • 当页面正在滑动中不断回调。
    • 可用于页面间动画联动、Tab 动画跟随效果等。
  • onPageScrollStateChanged(int state)
    • 页面滑动状态变化时回调。
    • state 可能的值:
      • SCROLL_STATE_IDLE(0): 空闲状态,页面滑动停止。
      • SCROLL_STATE_DRAGGING(1): 用户正在滑动。
      • SCROLL_STATE_SETTLING(2): 页面正在自动滑动到最终位置。

4. 动态添加标签

TabLayout tabLayout = findViewById(binding.tabLayout);// 添加文本标签
tabLayout.addTab(tabLayout.newTab().setText("首页"));// 添加图标标签
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.ic_home));// 添加自定义视图标签
TabLayout.Tab tab = tabLayout.newTab();
tab.setCustomView(R.layout.custom_tab_view);
tabLayout.addTab(tab);
类型方法适用场景
文本标签setText("标题")简洁的标题导航
图标标签setIcon(R.drawable.xxx)图标导航栏(常见于底部)
自定义标签setCustomView(R.layout.xxx)图文结合、动画、徽章等复杂样式

5. 标签选择监听

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {@Overridepublic void onTabSelected(TabLayout.Tab tab) {// 标签被选中时触发int position = tab.getPosition();viewPager2.setCurrentItem(position);}@Overridepublic void onTabUnselected(TabLayout.Tab tab) {// 标签取消选中时触发}@Overridepublic void onTabReselected(TabLayout.Tab tab) {// 已选中标签再次点击时触发}
});

6. 禁用 ViewPager2 左右滑动翻页

//false表示禁止,true表示允许
binding.viewPager.setUserInputEnabled(false);

ViewPager2 内部是通过 RecyclerView 实现滑动的。setUserInputEnabled(false) 会禁用一切手势滑动,但不会影响通过 setCurrentItem() 代码设置页面切换。

http://www.lryc.cn/news/596950.html

相关文章:

  • Android用户鉴权实现方案深度分析
  • react18更新哪些东西
  • Nginx和Apache的区别
  • Apache PDFBox深入实践
  • Apache JMeter 使用记录踩坑
  • MCP客户端架构与实施
  • Apache POI 介绍与使用指南
  • apache-doris安装兼datax-web配置
  • LNMP-zblog分布式部署
  • 【Unity Shader】Special Effects(十二)Glow 外发光(UI)
  • Unity × RTMP × 头显设备:打造沉浸式工业远控视频系统的完整方案
  • 如何在macOS上修改iPhone的定位
  • ESP32的ADF详解:5. Streams的API
  • 聊聊 Flutter 在 iOS 真机 Debug 运行出现 Timed out *** to update 的问题
  • 在AI深度嵌入企业业务的当下——AI时代的融合数据库
  • Qt 菜单与工具栏设计:提升用户体验
  • AI产品经理面试宝典第48天:产品设计与用户体验优化策略
  • 【数学建模 | Matlab】二维绘图 和 三维绘图
  • 国产数据库转向 “融合” 赛道:电科金仓的下一代形态定义之路
  • Leetcode力扣解题记录--第240题(矩阵搜索)
  • 大规模金融数据相关性并行计算系统设计与实现
  • Linux内存映射原理
  • 第十讲:stack、queue、priority_queue以及deque
  • Python-初学openCV——图像预处理(一)
  • 如何在 npm 上发布 Element Plus 二次封装组件
  • Parasoft为金融服务打造统一测试平台,提升安全、合规与交付效率
  • C++ Primer(第5版)- Chapter 7. Classes -005
  • ESP32的ADF详解:6. Audio Processing的API
  • Android 测试全指南:单元测试与UI测试框架详解
  • ESP-NOW实战:ESP32一对多无线通信方案(支持ESP8266兼容)