ViewPager 与导航
本教程共 100 篇 · 第 62 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
62. ViewPager 与导航
本节目标:学会用 ViewPager2 实现页面滑动切换、TabLayout 做标签页、BottomNavigationView 做底部导航、Navigation Drawer 做侧滑菜单。
这些是传统 View 体系的导航组件。在 Compose 项目里,这些功能用 Navigation Compose + Scaffold 实现更简单。但老项目和 AndroidView 嵌入场景仍需要它们。
ViewPager2
ViewPager2 是页面滑动容器,用户左右滑动切换页面。它内部用 RecyclerView 实现,比老的 ViewPager 性能更好。
添加依赖
dependencies {
implementation("androidx.viewpager2:viewpager2:1.1.0")
}
基本用法
class PagerAdapter(
private val fragments: List<Fragment>,
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment = fragments[position]
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val fragments = listOf(HomeFragment(), SearchFragment(), ProfileFragment())
binding.viewPager.adapter = PagerAdapter(fragments, supportFragmentManager, lifecycle)
}
}
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
设置方向
binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL // 默认
// ORIENTATION_VERTICAL 竖直滑动
页面变化监听
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
// 页面切换时回调
}
})
Note
FragmentStateAdapter是 ViewPager2 的 Fragment 适配器,用法和 RecyclerView 的 Adapter 类似。每个页面是一个 Fragment。如果页面内容简单(不需要 Fragment),也可以用RecyclerView.Adapter直接绑定 View。
TabLayout 标签页
TabLayout 和 ViewPager2 配合,做顶部标签页切换:
dependencies {
implementation("com.google.android.material:material:1.12.0")
}
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
联动
val titles = listOf("首页", "搜索", "我的")
titles.forEach { title ->
binding.tabLayout.addTab(binding.tabLayout.newTab().setText(title))
}
// TabLayoutMediator 自动同步两者状态
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
tab.text = titles[position]
}.attach()
Tip
TabLayoutMediator是关键:它自动处理 TabLayout 和 ViewPager2 的双向同步。点 Tab 切换页面,滑动页面切换 Tab,不用手动写监听逻辑。
BottomNavigationView 底部导航
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottomNav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
菜单文件 res/menu/bottom_nav_menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_home"
android:title="首页" />
<item
android:id="@+id/nav_search"
android:icon="@drawable/ic_search"
android:title="搜索" />
<item
android:id="@+id/nav_profile"
android:icon="@drawable/ic_person"
android:title="我的" />
</menu>
配合 Fragment 切换
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (savedInstanceState == null) {
switchFragment(HomeFragment())
}
binding.bottomNav.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_home -> switchFragment(HomeFragment())
R.id.nav_search -> switchFragment(SearchFragment())
R.id.nav_profile -> switchFragment(ProfileFragment())
}
true
}
}
private fun switchFragment(fragment: Fragment) {
supportFragmentManager.commit {
replace(R.id.container, fragment)
}
}
}
Note
setOnItemSelectedListener返回true表示选中该项。如果返回false,Tab 不会高亮。底部导航配合 ViewPager2 也很常见:滑动和点 Tab 都能切换页面。
Navigation Drawer 侧滑抽屉
侧滑菜单,从屏幕左侧滑出。
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 主内容 -->
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 侧滑菜单 -->
<com.google.android.material.navigation.NavigationView
android:id="@+id/navView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/drawer_menu"
app:headerLayout="@layout/drawer_header" />
</androidx.drawerlayout.widget.DrawerLayout>
代码控制
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 工具栏的导航按钮打开抽屉
binding.toolbar.setNavigationOnClickListener {
binding.drawerLayout.openDrawer(GravityCompat.START)
}
// 菜单项点击
binding.navView.setNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_home -> switchFragment(HomeFragment())
R.id.nav_settings -> switchFragment(SettingsFragment())
}
binding.drawerLayout.closeDrawer(GravityCompat.START)
true
}
// 返回键:抽屉打开时先关抽屉,否则走默认返回
onBackPressedDispatcher.addCallback(this) {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
isEnabled = false
onBackPressedDispatcher.onBackPressed()
}
}
}
}
Warning
onBackPressed()在 Android 13+ 已废弃,配合预测式返回手势要用OnBackPressedDispatcher.addCallback()。上面把回调注册放到onCreate里,返回键先尝试关抽屉,关不掉再交回系统处理。
layout_gravity="start"决定抽屉从哪边滑出。start是左侧(支持 RTL 布局),end是右侧。点击菜单项后记得closeDrawer()关闭抽屉,否则它会一直开着。
Tip
- Navigation Drawer 也可以配合 Navigation Component(Jetpack 导航组件)使用,官方推荐用
NavigationUI自动同步菜单和导航图。但手写 Fragment 切换更直观,适合简单场景。
小结
ViewPager2 做页面滑动,配 TabLayout 做标签页(用 TabLayoutMediator 联动)。BottomNavigationView 做底部导航,配合 Fragment 切换。Navigation Drawer 做侧滑菜单,用 DrawerLayout + NavigationView。在 Compose 项目里,这些功能用 Scaffold + Navigation Compose 更简洁,但老项目维护仍需掌握。下一节学习菜单与对话框。