当使用BottomNavigationView和新的NavController时,有没有办法保持片段活着?

IdA*_*dro 55 navigation android bottomnavigationview

我正在尝试使用新的导航组件.我使用带有navController的BottomNavigationView:NavigationUI.setupWithNavController(bottomNavigation,navController)

但是当我切换片段时,即使它们以前被使用过,它们也会每次都被破坏/创建.

有没有办法让我们的主要片段链接到我们的BottomNavigationView?

STA*_*ERO 40

试试这个.

航海家

创建自定义导航器.

@Navigator.Name("custom_fragment")  // Use as custom tag at navigation.xml
class CustomNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

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

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = destination.createFragment(args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }
}
Run Code Online (Sandbox Code Playgroud)

NavHostFragment

创建自定义NavHostFragment.

class CustomNavHostFragment: NavHostFragment() {
    override fun onCreateNavController(navController: NavController) {
        super.onCreateNavController(navController)
        navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id)
    }
}
Run Code Online (Sandbox Code Playgroud)

navigation.xml

使用自定义标记而不是片段标记.

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation"
    app:startDestination="@id/navigation_first">

    <custom_fragment
        android:id="@+id/navigation_first"
        android:name="com.example.sample.FirstFragment"
        android:label="FirstFragment" />
    <custom_fragment
        android:id="@+id/navigation_second"
        android:name="com.example.sample.SecondFragment"
        android:label="SecondFragment" />
</navigation>
Run Code Online (Sandbox Code Playgroud)

活动布局

使用CustomNavHostFragment而不是NavHostFragment.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="com.example.sample.CustomNavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>
Run Code Online (Sandbox Code Playgroud)

更新

我创建了示例项目.链接

我不创建自定义NavHostFragment.我用navController.navigatorProvider += navigator.

  • @ jL4,我有同样的问题,可能你忘了从你的活动的NavHostFragment中删除`app:navGraph`. (4认同)
  • NavigationComponent应该成为一个解决方案,而不是另一个问题,因此程序员必须创建这样的解决方法。 (4认同)
  • @SyedAhmedJamil 我的答案是旧的。因此,请检查此存储库 https://github.com/STAR-ZERO/navigation-keep-fragment-sample (4认同)
  • 虽然这个解决方案有效("片段"被重用,而不是重新创建),但导航存在问题.`bottomnavigationview`中的每个`menuitem`都被认为是主要的 - 因此,当按下'BACK`时,应用程序结束(或转到顶部组件).更好的解决方案就是像YouTube应用程序一样:(1)点击主页; (2)点击趋势; (3)点击收件箱; (4)点击趋势; (5)点击"返回" - 进入收件箱; (6)点击`BACK` - 进入Home; (7)点击"返回" - 应用程序存在.总之,"`menuitems`"之间的YouTube"BACK"功能可以追溯到最新版本,无需重复项目. (2认同)
  • 如何保留后退按钮功能?按下后退按钮时,应用程序正在完成。 (2认同)
  • 我面临与@Francis 相同的问题。即使启用了`app:defaultNavHost="true"`,当按下后退按钮时,应用程序也会退出 (2认同)
  • 为什么Google不开箱即用呢? (2认同)
  • @STAR_ZERO 顺便说一句,在您发布答案后,`FragmentManager` 内部是否发生了变化?因为我在片段导航器类中的任何位置都找不到“dispatchOnNavigatorNaviged()”方法。虽然即使没有这种方法我也能实现它,但我只是好奇。你能看一下 `FragmentNavigator` 并让我知道我是对还是错吗? (2认同)
  • @Arsenius 因为他们能做的最好的事情就是创建糟糕的组件,在每次会议上谈论它,让它看起来像一个解决方案,而实际上它只是另一个柔性胶带来修复 Android 中的根本设计缺陷 (2认同)

Pio*_*rus 15

经过几个小时的研究,我找到了解决方案。它一直在我们面前:) 有一个函数:popBackStack(destination, inclusive)如果在 backStack 中找到,它导航到给定的目的地。它返回布尔值,因此如果控制器找不到片段,我们可以手动导航到那里。

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }
Run Code Online (Sandbox Code Playgroud)

  • 如何以及在哪里使用它?如果我从片段 A 导航到 B,然后从 B 导航到 A,如果不调用 oncraeteView() 和 onViewCreated() 方法,简而言之,不重新启动片段 A,我怎么能去呢? (5认同)

Zak*_*nov 6

Google样本链接 仅将NavigationExtensions复制到您的应用程序并通过示例进行配置。效果很好。

  • 但它仅适用于底部导航。如果你有一般的导航,你就有问题了 (3认同)
  • Nav 版本 2.5.3:唯一的主图 startDestination 目标片段保留在内存中,其他片段仍然已重新创建。无用的修复,谷歌在这里也不好。 (2认同)