首页 / Android 入门教程 / ViewPager 与导航

Android 入门教程

ViewPager 与导航

本教程共 100 篇 · 第 62 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程ViewPager2TabLayoutBottomNavigationViewNavigationDrawer

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 标签页

TabLayoutViewPager2 配合,做顶部标签页切换:

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 都能切换页面。

侧滑菜单,从屏幕左侧滑出。

<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 更简洁,但老项目维护仍需掌握。下一节学习菜单与对话框。