目录
二、修改主页面布局文件(activity_main.xml)
三、创建页面适配器(MainPagerAdapter.java)
步骤 4:底部导航栏点击 → 切换 ViewPager2 页面(单向联动)
步骤 5:ViewPager2 滑动 → 同步底部导航栏选中状态(双向联动)
一、导入依赖
在模块级 build.gradle 中注入依赖。
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
implementation("androidx.viewpager2:viewpager2:1.0.0")//注入ViewPager2依赖
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
二、修改主页面布局文件(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/bottom_nav_menu"
app:itemIconTint="@null"
app:itemIconSize="20dp" //新增标签尺寸
app:labelVisibilityMode="unlabeled" //标签不可见 />
</androidx.constraintlayout.widget.ConstraintLayout>
将原来的 FrameLayout 容器换成 androidx.viewpager2.widget.ViewPager2 容器,并且重新赋予 id 为 view_pager 。
三、创建页面适配器(MainPagerAdapter.java)
三、优化 MainActivity(主页面)的逻辑
核心优化是引入 ViewPager2 组件,把之前的「Fragment 直接替换」改成了 ViewPager2 + Fragment 联动底部导航栏 的架构,并通过「导航栏点击 → 切换 ViewPager2 页面」和「ViewPager2 滑动 → 同步导航栏选中状态」的双向联动,实现更流畅的页面切换体验。
这种方案更适合多页面切换场景(如多导航标签),支持滑动切换页面、页面状态缓存,且底部导航栏与页面状态完全同步。
1、导入 ViewPager2
ViewPager2 是 AndroidX 提供的 滑动页面容器组件,专门用于管理多个 Fragment(或 View),支持左右滑动切换、页面缓存、垂直滑动等功能,比旧版 ViewPager 更稳定、功能更强。
import androidx.viewpager2.widget.ViewPager2; // 新增:ViewPager2 组件(谷歌推荐的滑动页面容器)
2、优化 onCreate 方法
onCreate 仍是活动初始化入口,核心逻辑新增了 ViewPager2 的配置和双向联动,我们分步骤拆解:
步骤 1:沉浸式布局初始化(和之前完全一致)
步骤 2:初始化 ViewPager2 并设置适配器
代码中注释说的适配器控件即是之前主页面的 Fragment 控件,现在替换为 ViewPager2 控件了。
// 1. 从布局中找到 ViewPager2 控件(对应 activity_main.xml 中的 <androidx.viewpager2.widget.ViewPager2>)
ViewPager2 viewPager = findViewById(R.id.view_pager);
// 2. 设置适配器(MainPagerAdapter 是自定义的适配器,负责给 ViewPager2 提供 Fragment 页面)
viewPager.setAdapter(new MainPagerAdapter(this));
// 3. 设置默认显示第 0 个页面(首页,对应 nav_home;false 表示无切换动画)
viewPager.setCurrentItem(0, false);
ViewPager2 本身不存储页面,需要通过适配器获取页面(类似列表 RecyclerView 的 Adapter)。MainPagerAdapter 是需要自定义的类,作用是:
- 管理 4 个 Fragment(Home/Market/Watchlist/Profile)的实例;
- 告诉 ViewPager2 总共有多少个页面;
- 当 ViewPager2 需要显示某个页面时,提供对应的 Fragment。
(补充:MainPagerAdapter 的典型实现会继承 FragmentStateAdapter,下面是示例)
步骤3:创建页面适配器
package com.我的包名.hc;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class MainPagerAdapter extends FragmentStateAdapter {
// 构造方法:接收一个 FragmentActivity 实例(如 MainActivity)
public MainPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity); // 调用父类构造方法,传入活动实例
}
/*
核心逻辑:根据页面位置(position),返回对应的 Fragment 实例。
调用时机:
当 ViewPager2 初始化时,会调用该方法创建默认显示的页面(如第 0 页 HomeFragment);
当用户滑动页面时,ViewPager2 会提前创建相邻页面(如当前在第 0 页,会预创建第 1 页),确保滑动流畅;
页面被销毁后(如滑动距离过远,超出缓存范围),再次需要显示时,会重新调用该方法创建 Fragment。
注意:position 必须和 MainActivity 中导航栏的对应关系一致(0 = 首页、1 = 市场、2 = 收藏、3 = 我的),否则会出现 “导航栏点击和页面不匹配” 的问题。
*/
// @NonNull:返回的 Fragment 不能为 null
@NonNull
@Override
public Fragment createFragment(int position) {
// position:页面位置(从 0 开始,对应 ViewPager2 的第 N 个页面)
switch (position) {
case 0:
return new HomeFragment(); // 第 0 页 → 首页 Fragment
case 1:
return new MarketFragment(); // 第 1 页 → 市场 Fragment
case 2:
return new WatchlistFragment(); // 第 2 页 → 收藏 Fragment
case 3:
return new ProfileFragment(); // 第 3 页 → 我的 Fragment
default:
return new HomeFragment(); // 异常位置(如 position 超出 0-3),默认返回首页
}
}
/*作用:告诉 ViewPager2 总共有多少个页面,ViewPager2 会根据这个数量:
计算滑动的边界(比如最多滑到第 3 页,不能再往右滑);
管理页面缓存(默认缓存相邻 1 个页面,总数影响缓存策略);
配合页面指示器(如 TabLayout)显示总页数。*/
@Override
public int getItemCount() {
return 4; // 总共有 4 个页面(对应 4 个导航标签)
}
}
这个自定义的 MainPagerAdapter 是 ViewPager2 专属的 Fragment 适配器,核心作用是「给 ViewPager2 提供 Fragment 页面、管理页面数量和创建逻辑」,是 ViewPager2 能正常显示多个页面的核心 “桥梁”。
它继承自谷歌官方提供的 FragmentStateAdapter(专门用于 ViewPager2 + Fragment 的适配器),结构非常简洁,只有 3 个核心部分:构造方法、createFragment(创建页面)、getItemCount(页面总数)。下面逐部分详细解释:
适配器的核心职责:
- 告诉 ViewPager2 总共有多少个页面(
getItemCount);- 当 ViewPager2 需要显示某个页面时,创建并返回对应的 Fragment(
createFragment);- 管理 Fragment 的生命周期(由
FragmentStateAdapter底层实现,无需手动处理)。
步骤 4:底部导航栏点击 → 切换 ViewPager2 页面(单向联动)
BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
bottomNav.setOnItemSelectedListener(item -> {
int id = item.getItemId(); // 获取点击标签的 ID
// 根据标签 ID,切换 ViewPager2 到对应的页面(position 从 0 开始)
if (id == R.id.nav_home) {
viewPager.setCurrentItem(0, false); // 第 0 页:首页(无动画)
return true; // 保持标签选中状态(图标切换生效)
} else if (id == R.id.nav_market) {
viewPager.setCurrentItem(1, false); // 第 1 页:市场
return true;
} else if (id == R.id.nav_watchlist) {
viewPager.setCurrentItem(2, false); // 第 2 页:收藏
return true;
} else if (id == R.id.nav_profile) {
viewPager.setCurrentItem(3, false); // 第 3 页:我的
return true;
}
return false;
});
和之前的区别:之前是 replaceFragment(new HomeFragment())方法(销毁旧 Fragment、创建新 Fragment),现在是 viewPager.setCurrentItem(position)(切换 ViewPager2 中的页面,Fragment 会被缓存,不会重复创建)。
setCurrentItem(position, false) 的第二个参数:是否显示页面切换动画(false 表示无动画,true 表示有滑动动画)。
步骤 5:ViewPager2 滑动 → 同步底部导航栏选中状态(双向联动)
// 给 ViewPager2 注册页面变化监听器(监听页面滑动/切换事件)
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
// 当页面被选中时触发(滑动结束后)
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
// 根据当前选中的页面位置,同步设置底部导航栏的选中标签
if (position == 0) bottomNav.setSelectedItemId(R.id.nav_home);
else if (position == 1) bottomNav.setSelectedItemId(R.id.nav_market);
else if (position == 2) bottomNav.setSelectedItemId(R.id.nav_watchlist);
else if (position == 3) bottomNav.setSelectedItemId(R.id.nav_profile);
}
});
核心作用是解决「滑动页面后,底部导航栏选中状态不更新」的问题。比如用户不点击导航栏,而是直接滑动屏幕切换页面,这时通过这个监听器,底部导航栏会自动选中对应标签(图标也会同步切换为选中态),保证 UI 状态一致。

被折叠的 条评论
为什么被折叠?



