You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Fragment中使用ViewPager与TabLayout?底部导航嵌套滑动实现

实现底部导航栏 + 嵌套ViewPager/TabLayout的完整方案

我来一步步帮你实现这个需求——底部导航栏切换顶层Fragment,每个顶层Fragment内部通过ViewPager+TabLayout管理子Fragment,这是移动端很常见的架构模式,下面直接上可落地的代码和关键细节:

一、先搞定底部导航栏与顶层Fragment的切换

这部分是基础,先让底部导航能正常切换不同的主Fragment。

1. 主页面布局(activity_main.xml)

用FrameLayout作为主Fragment的容器,搭配BottomNavigationView:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- 承载主Fragment的容器 -->
    <FrameLayout
        android:id="@+id/main_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <!-- 底部导航栏 -->
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_nav_menu" />

</LinearLayout>

2. 底部导航菜单(res/menu/bottom_nav_menu.xml)

定义几个导航项,比如首页、发现、我的:

<?xml version="1.0" encoding="utf-8"?>
<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_discovery"
        android:icon="@drawable/ic_discovery"
        android:title="发现" />
    <item
        android:id="@+id/nav_profile"
        android:icon="@drawable/ic_profile"
        android:title="我的" />
</menu>

3. 主Activity的切换逻辑

在MainActivity里处理底部导航的点击事件,切换对应的主Fragment。如果需要保留Fragment状态,推荐用add+隐藏显示的方式,而不是replace:

class MainActivity : AppCompatActivity() {
    // 提前初始化所有主Fragment,避免重复创建
    private val homeFragment = HomeMainFragment()
    private val discoveryFragment = DiscoveryMainFragment()
    private val profileFragment = ProfileMainFragment()
    private var currentFragment: Fragment = homeFragment

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化所有主Fragment,默认显示首页
        supportFragmentManager.beginTransaction()
            .add(R.id.main_fragment_container, homeFragment)
            .add(R.id.main_fragment_container, discoveryFragment)
            .add(R.id.main_fragment_container, profileFragment)
            .hide(discoveryFragment)
            .hide(profileFragment)
            .commit()

        // 底部导航点击事件
        findViewById<BottomNavigationView>(R.id.bottom_nav).setOnItemSelectedListener { item ->
            when (item.itemId) {
                R.id.nav_home -> switchFragment(currentFragment, homeFragment)
                R.id.nav_discovery -> switchFragment(currentFragment, discoveryFragment)
                R.id.nav_profile -> switchFragment(currentFragment, profileFragment)
                else -> false
            }
        }
    }

    // 切换Fragment的工具方法
    private fun switchFragment(from: Fragment, to: Fragment): Boolean {
        if (currentFragment != to) {
            currentFragment = to
            supportFragmentManager.beginTransaction()
                .hide(from)
                .show(to)
                .commit()
        }
        return true
    }
}

二、在主Fragment内部实现ViewPager+TabLayout切换子Fragment

这是核心部分,每个主Fragment(比如HomeMainFragment)内部嵌套自己的ViewPager和TabLayout,用来切换子Fragment。

1. 主Fragment的布局(fragment_home_main.xml)

包含TabLayout和ViewPager(如果用新版推荐ViewPager2,后面会补充):

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- 顶部标签栏 -->
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="fixed"
        app:tabGravity="fill" />

    <!-- 子Fragment容器 -->
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

2. 创建子Fragment

先准备几个子Fragment,比如HomeSubFragment1、HomeSubFragment2,布局可以很简单:

class HomeSubFragment1 : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home_sub1, container, false)
    }
}

3. 自定义ViewPager适配器

⚠️ 关键:在Fragment内部嵌套子Fragment时,必须用childFragmentManager,不能用fragmentManager,否则子Fragment的生命周期会异常!

class HomeViewPagerAdapter(fragment: Fragment) : FragmentPagerAdapter(fragment.childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

    // 标签标题
    private val tabTitles = arrayOf("推荐", "热门", "最新")
    // 子Fragment列表
    private val fragmentList = listOf(
        HomeSubFragment1(),
        HomeSubFragment2(),
        HomeSubFragment3()
    )

    override fun getCount(): Int = fragmentList.size

    override fun getItem(position: Int): Fragment = fragmentList[position]

    override fun getPageTitle(position: Int): CharSequence? = tabTitles[position]
}

4. 主Fragment的逻辑代码

把TabLayout和ViewPager绑定起来:

class HomeMainFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_home_main, container, false)
        val tabLayout = view.findViewById<TabLayout>(R.id.tab_layout)
        val viewPager = view.findViewById<ViewPager>(R.id.view_pager)

        // 设置适配器
        val adapter = HomeViewPagerAdapter(this)
        viewPager.adapter = adapter
        // 绑定TabLayout和ViewPager
        tabLayout.setupWithViewPager(viewPager)

        // 可选:设置预加载页面数,避免频繁创建销毁
        viewPager.offscreenPageLimit = 2

        return view
    }
}

三、升级方案:用ViewPager2替代ViewPager

ViewPager2是官方推荐的升级版,支持横向/纵向滑动,更稳定。这里快速给出适配代码:

1. 布局替换为ViewPager2

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/view_pager2"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

2. 自定义FragmentStateAdapter

class HomeViewPager2Adapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    private val tabTitles = arrayOf("推荐", "热门", "最新")
    private val fragmentList = listOf(
        HomeSubFragment1(),
        HomeSubFragment2(),
        HomeSubFragment3()
    )

    override fun getItemCount(): Int = fragmentList.size

    override fun createFragment(position: Int): Fragment = fragmentList[position]

    // 提供标签标题
    fun getTabTitle(position: Int): String = tabTitles[position]
}

3. 主Fragment中绑定TabLayout和ViewPager2

需要用TabLayoutMediator来绑定:

class HomeMainFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_home_main, container, false)
        val tabLayout = view.findViewById<TabLayout>(R.id.tab_layout)
        val viewPager2 = view.findViewById<ViewPager2>(R.id.view_pager2)

        val adapter = HomeViewPager2Adapter(this)
        viewPager2.adapter = adapter

        // 绑定TabLayout和ViewPager2
        TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
            tab.text = adapter.getTabTitle(position)
        }.attach()

        return view
    }
}

四、关键注意事项

  • 嵌套Fragment的FragmentManager选择:必须用childFragmentManager,否则子Fragment会和主Fragment共用同一个管理器,导致生命周期混乱。
  • 状态保持:底部导航切换时用add+隐藏显示的方式,能保留主Fragment的状态(比如ViewPager的滑动位置)。
  • 性能优化:设置offscreenPageLimit预加载页面,减少Fragment的频繁创建销毁。

内容的提问来源于stack exchange,提问作者user11280870

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:53:15