【问题标题】:OnDestory() being called when switching back to fragment with NavigationUI使用 NavigationUI 切换回片段时调用 OnDestory()
【发布时间】:2020-09-17 16:28:21
【问题描述】:

我正在使用带有底部导航视图的 NavigationUI,并且还使用操作栏进行了设置。

我的应用程序从主片段开始,当我切换到我的搜索片段 OnDestroyView() 时,homeFragment(预期)被调用,但是当我从搜索片段切换回我的主片段时,onDestroy() 被调用并且然后显示homefragment 的一个新实例,而不是它恢复到我离开它的状态的那个实例。如何让它恢复到之前的状态?

这就是我设置 NavigationUI 的方式:

NavController navController = Navigation.findNavController(this,
         R.id.navigation_host_fragment);

NavigationUI.setupWithNavController(mainBinding.mainActivityBn, navController);
NavigationUI.setupActionBarWithNavController(this,navController);

我的导航图:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_activity_navigation"
app:startDestination="@id/homeFragment">

  <fragment
    android:id="@+id/homeFragment"
    android:name=<name>
    android:label="Home"
    tools:layout="@layout/fragment_home" />

  <fragment
    android:id="@+id/serachFragment"
    android:name=<name>
    android:label="Search"
    tools:layout="@layout/fragment_search" />

 </navigation>

编辑:我正在使用底部导航视图在片段之间导航。当我按下后退按钮时,主页片段将按之前的状态加载(预期行为)。

【问题讨论】:

  • 你如何切换回你的主页片段(通过点击系统返回按钮?通过点击底部导航上的那个项目?)?你能包括你的导航图吗?
  • @ianhanniballake 使用底部导航视图
  • 你能包括你的导航图吗?
  • 澄清一下,当您点击系统返回按钮时,您是否看到了不同的行为?
  • 是的@ianhanniballake 后退按钮显示主页片段而不重新加载它。当我也点击底部导航上的主页时,这就是我想要做的事情

标签: android android-fragments bottomnavigationview android-navigation


【解决方案1】:

根据this issue

要理解这里的逻辑,我们需要看看NavigationUI 在选择目的地时做了什么。查看the source code 并删除不相关的代码(动画、二级菜单代码):

NavOptions.Builder builder = new NavOptions.Builder()
  .setLaunchSingleTop(true);
builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
NavOptions options = builder.build();
navController.navigate(item.getItemId(), null, options);

所以popUpTo 弹出到根目标,但使用false 不弹出根目标本身。然后它使用setLaunchSingleTop(true) 来避免在后堆栈上有两个起始目标实例。此处的目的是在您返回该根目的地时不清除与该根目的地关联的任何 ViewModelSavedStateHandle

因此,NavController 调用 navigate(),我们将所有内容弹出,但不包括返回堆栈中的起始目的地(您的第一个屏幕),正确移除第二个屏幕。

NavController 然后调用FragmentNavigator 来执行navigate() 调用。 FragmentNavigator 看到 launchSingleTop 为真,并正确确定片段已经在堆栈顶部。然而,与活动不同,片段没有任何等效于onNewIntent()onNewArguments() 来模仿singleTop 行为,因此FragmentNavigator 弹出前一个片段并添加片段的新实例,传入新参数(这里,只是null)。

因此,由于 Fragment 处理 setLaunchSingleTop(true) 标志的方式,单击底部导航中的第一项会再次破坏并重新创建 Fragment。这就是系统后退按钮行为不同的原因 - 系统后退按钮只是调用 popBackStack() 而没有使用任何单一顶部行为。

该问题仍然存在并表明:

FragmentNavigator 单顶的行为很糟糕,应该感觉很糟糕。它与活动的行为不匹配并且不直观。如果您不介意,让我们使用此错误来跟踪该工作。理想情况下,会创建一个新实例,这意味着您的原始 MapFragment 将被恢复并立即回到原来的位置。这与您点击系统后退按钮时发生的情况一致。

该问题没有直接的解决方法,因此最好的结果(不会发生这种情况)需要您为该问题加注星标并等待它得到修复。但是,您会注意到您保存到 NavBackStackEntry 中的任何状态或 ViewModel 将通过 singleTop 操作保存:

// Instead of using this:
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);

// You could use:
NavController navController = NavHostFragment.findNavController(this);
NavBackStackEntry entry = navController.getBackStackEntry(R.id.homeFragment);
myViewModel = new ViewModelProvider(entry).get(MyViewModel.class);

但是,这无助于保存 Fragment 的视图状态(即滚动位置),因为它们无法保存到 NavBackStackEntry 中,而只能 保存在 Fragment 级别。

【讨论】:

    猜你喜欢
    • 2022-01-07
    • 1970-01-01
    • 2018-01-20
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多