为什么Android Navigation Component screen 没有返回到上一个Fragment,而是调用了上一个Fragment 的onViewCreated 中的方法?

Why Android Navigation Component screen not go back to previous Fragment,but a method in onViewCreated of previos Fragment being called?

我有 2 个片段调用 CreateRoomFragmentDisplayPhotoFragment,导航图如下所示:

<navigation>    
<fragment
    android:id="@+id/createRoomFragment"
    android:name="package.room.CreateRoomFragment"
    android:label="Create a room"
    tools:layout="@layout/fragment_create_room">
    <action
        android:id="@+id/action_createRoomFragment_to_roomFragment"
        app:destination="@id/roomFragment" />
    <action
        android:id="@+id/action_createRoomFragment_to_displayPhotoFragment"
        app:destination="@id/displayPhotoFragment" />
</fragment>

<fragment
    android:id="@+id/displayPhotoFragment"
    android:name="package.fragment.DisplayPhotoFragment"
    android:label="fragment_display_photo"
    tools:layout="@layout/fragment_display_photo" >
    <argument android:name="bitmap"
              app:argType="android.graphics.Bitmap"/>
</fragment>

所以当我想从 CreateRoomFragment 移动到 DisplayPhotoFragment 时,我使用如下操作:

NavDirections action = CreateRoomFragmentDirections.actionCreateRoomFragmentToDisplayPhotoFragment(selectedPhoto);
Navigation.findNavController(view).navigate(action);

这样做,我可以导航到 DisplayPhotoFragment

但是当我按下设备的 back 按钮以及工具栏上的 Back arrow 时,它无法返回到 CreateRoomFragment

我试过了,但还是无法回到之前的片段:

requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(),
                new OnBackPressedCallback(true) {
                    @Override
                    public void handleOnBackPressed() {

                       navController.navigateUp();  //I tried this 
                       navController.popBackStack(R.id.createRoomFragment,false); //and also this
                    }
                });

现在的主要问题:

使用上面的代码,画面并没有回到之前的Fragment(CreateRoomFragment),还是卡在DisplayPhotoFragment,但是同时,一个API的方法在 CreateRoomFragment onViewCreated 节中被调用。

这是什么原因造成的?我该如何解决这个问题?

Android 维护一个包含您访问过的目的地的返回堆栈。当用户打开应用程序时,您的应用程序的第一个目的地被放置在堆栈上。每次调用 navigate() 方法都会将另一个目的地放在堆栈顶部。点击向上或向后分别调用 NavController.navigateUp()NavController.popBackStack() 方法,以从堆栈中删除(或弹出)顶部目标。

NavController.popBackStack() returns 一个布尔值,指示它是否成功弹出回另一个目的地。此 returns 错误的最常见情况是您手动弹出图形的起始目的地。

当方法returns为假时,NavController.getCurrentDestination()returns为空。您负责导航到新目的地或通过在 Activity.

上调用 finish() 来处理弹出消息

使用操作导航时,您可以选择使用操作的 popUpTopopUpToInclusive 参数从返回堆栈中弹出其他目的地。

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    CustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

我遇到了同样的问题。对我来说,问题是我使用 LiveData 布尔值来决定何时转到下一个片段。当我然后导航 back/up 时,布尔值仍然为真,因此它会自动再次向前导航。

使用实时数据处理导航的最佳解决方案是使用 SingleLiveEvent。

您可以随时使用此 class,它是 MutableLiveData 的扩展。

https://gist.githubusercontent.com/cdmunoz/ebe5c4104dadc2a461f512ea1ca71495/raw/a17f76754f86a4c0b1a6b43f5c6e6d179535e627/SingleLiveEvent.kt

有关此检查的详细信息 运行:

https://proandroiddev.com/singleliveevent-to-help-you-work-with-livedata-and-events-5ac519989c70

有类似的问题。我们仍然有多个带有导航组件的活动。 所以想象一下 activity A -> activity B,activity B 有自己的导航和片段。当初始片段试图弹出返回堆栈时,无处可弹出,导航控制器不知道要完成 activity。所以我找到的一个解决方案是

if (!findNavController().popBackStack()) activity?.finish()

如果导航控制器无法弹出,它将完成 activity。

如果您只想观察一次事件,可以在 MutableLiveData 上使用 MutableSharedFlow

在你的 viewModel:

 private val _events = MutableSharedFlow<Event>()
    val events = _events.asSharedFlow() // read-only public view

    suspend fun postEvent() {
        _events.emit(event) // suspends until subscribers receive it
    }

在你的 Activity/Fragment class:

lifecycleScope.launchWhenStarted {
   viewModel.events.collect {
   }
}

viewModel.postEvent()

这将防止在返回片段时继续观察数据。