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

使用NavGraph结合Bottom Navigation时阻止Fragment重复刷新的方案

问题

我目前使用NavigationGraph配合底部导航栏,每次点击底部导航按钮时Fragment都会被重建。我希望像Twitter、Instagram这类应用一样,当Fragment已经创建过之后,切换底部导航时不再刷新该Fragment。现在返回MainFragment时,所有视图都会重新创建,还会触发不必要的数据库拉取操作。我查过相关问题但没找到合适的方案,求建议。

截图说明:从其他Fragment返回MainFragment时,MainFragment会被重新创建。

相关代码

MainActivity 代码

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

    navController = findNavController(R.id.main_fragmentContainerView)
    val appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.mainFragment,
            R.id.searchFragment,
            R.id.userProfileFragment
        )
    )

    bottomNavigationView.setupWithNavController(navController)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

override fun onNavigateUp(): Boolean {
    return navController.navigateUp() || super.onSupportNavigateUp()
}

MainFragment 代码

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    readDatabase()
}
解决方案

Navigation组件默认的setupWithNavController每次切换都会重建Fragment,要实现保留实例的效果,推荐以下几种方案:

方案1:自定义底部导航逻辑(最稳妥)

放弃Navigation组件自带的绑定逻辑,自己管理Fragment的切换与实例缓存,完全控制生命周期:

  1. 把布局里的NavHostFragment替换成普通的FragmentContainerView(或FrameLayout),id设为fragment_container。
  2. 在MainActivity中维护Fragment实例缓存,切换时只做show/hide操作:
class MainActivity : AppCompatActivity() {
    private val fragmentCache = mutableMapOf<Int, Fragment>()
    private lateinit var bottomNav: BottomNavigationView
    private val containerId = R.id.fragment_container

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bottomNav = findViewById(R.id.bottomNavigationView)

        // 初始化默认显示的Fragment
        if (savedInstanceState == null) {
            val mainFragment = MainFragment()
            fragmentCache[R.id.mainFragment] = mainFragment
            supportFragmentManager.beginTransaction()
                .add(containerId, mainFragment, "main")
                .commit()
            bottomNav.selectedItemId = R.id.mainFragment
        }

        // 设置底部导航点击事件
        bottomNav.setOnItemSelectedListener { menuItem ->
            val targetId = menuItem.itemId
            // 从缓存取Fragment,不存在则创建
            val targetFragment = fragmentCache.getOrPut(targetId) {
                when(targetId) {
                    R.id.mainFragment -> MainFragment()
                    R.id.searchFragment -> SearchFragment()
                    R.id.userProfileFragment -> UserProfileFragment()
                    else -> MainFragment()
                }
            }

            // 切换Fragment:隐藏其他,显示目标
            supportFragmentManager.beginTransaction().apply {
                fragmentCache.values.forEach { hide(it) }
                if (!targetFragment.isAdded) {
                    add(containerId, targetFragment, targetId.toString())
                }
                show(targetFragment)
                setPrimaryNavigationFragment(targetFragment)
                commit()
            }
            true
        }
    }
}

这种方式切换时Fragment不会重建,只会触发onHiddenChanged,完美保留之前的视图状态和数据,没有额外的数据库请求。

方案2:修改Navigation默认行为(有兼容性风险)

如果一定要保留NavigationGraph的使用,可以自定义FragmentNavigator来复用已有的Fragment实例,但要注意和其他Navigation功能的兼容性:

  1. 创建自定义Navigator:
class KeepStateFragmentNavigator(
    context: Context,
    manager: FragmentManager,
    containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = instantiateFragment(context, manager, destination.className, args)
            transaction.add(containerId, fragment, tag)
        }

        // 隐藏其他已加载的Fragment
        manager.fragments.forEach {
            if (it.tag != tag) transaction.hide(it)
        }
        transaction.show(fragment)
        transaction.setPrimaryNavigationFragment(fragment)
        transaction.commit()

        return destination
    }
}
  1. 在MainActivity中替换默认的Navigator:
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_fragmentContainerView) as NavHostFragment
    val navController = navHostFragment.navController

    // 替换默认的FragmentNavigator
    val customNavigator = KeepStateFragmentNavigator(this, supportFragmentManager, R.id.main_fragmentContainerView)
    navController.navigatorProvider.addNavigator(customNavigator)
    // 重新加载导航图
    navController.setGraph(R.navigation.nav_graph)

    val appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.mainFragment,
            R.id.searchFragment,
            R.id.userProfileFragment
        )
    )
    bottomNavigationView.setupWithNavController(navController)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

这种方式能保留Navigation的大部分特性,但可能和返回栈、深层链接等功能冲突,需要额外测试。

临时优化:避免重复请求数据

如果暂时不想改动导航逻辑,先修改MainFragment,只在首次创建时拉取数据:

class MainFragment : Fragment() {
    private var isDataLoaded = false

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 仅首次创建时加载数据
        if (!isDataLoaded) {
            readDatabase()
            isDataLoaded = true
        }
    }

    // 若需要在Fragment显示时刷新数据,可重写此方法
    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        if (!hidden) {
            // 可选:当Fragment重新显示时刷新数据
            // readDatabase()
        }
    }
}

这种方法只是避免了重复的数据库请求,但Fragment视图仍会重建,属于临时解决方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:00:54