0

我正在编写一个单一的活动应用程序,它使用 Android 的导航组件来帮助导航和片段场景进行仪器测试。在使用片段场景的仪器测试期间,在实际应用程序导航行为和被隔离测试的片段行为之间使用后退按钮时,我遇到了性能差异。

在我的 MainActivity 中,我有一个占据整个屏幕的主要 NavHostFragment。我使用导航主机片段来显示几个屏幕,包括一些主细节片段。每个主细节片段中都有另一个 NavHostFragment 以显示该功能的不同细节片段。此设置效果很好,并提供了我想要的行为。

为了完成主细节屏幕,我使用具有两个 FrameLayouts 的 ParentFragment 来为平板电脑和手机创建分屏,我以编程方式隐藏其中一个 FrameLayouts。创建 ParentFragment 时,它会检测它是否正在平板电脑或手机上运行,​​然后以编程方式将 NavHostFragment 添加到平板电脑的右侧框架布局中,并在手机隐藏右侧窗格中将 NavHostFragment 添加到左侧窗格中。NavHostFragments 还设置了不同的导航图,具体取决于它们是在平板电脑还是手机上运行(在手机上我们将片段显示为对话框,在平板电脑上我们将它们显示为常规片段)。

 private fun setupTabletView() {
        viewDataBinding.framelayoutLeftPane.visibility = View.VISIBLE
        if (navHostFragment == null) {
            navHostFragment = NavHostFragment.create(R.navigation.transport_destinations_tablet)
            navHostFragment?.let {
                childFragmentManager.beginTransaction()
                    .add(R.id.framelayout_left_pane, it, TRANSPORT_NAV_HOST_TAG)
                    .setPrimaryNavigationFragment(it)
                    .commit()
            }
        }

        if (childFragmentManager.findFragmentByTag(SummaryFragment.TAG) == null) {
            childFragmentManager.beginTransaction()
                .add(R.id.framelayout_right_pane, fragFactory.instantiate(ClassLoader.getSystemClassLoader(), SummaryFragment::class.java.canonicalName!!), SummaryFragment.TAG)
                .commit()
        }
    }

    private fun setupPhoneView() {
        viewDataBinding.framelayoutLeftPane.visibility = View.GONE
        if (navHostFragment == null) {
            navHostFragment = NavHostFragment.create(R.navigation.transport_destinations_phone)
            navHostFragment?.let {
                childFragmentManager.beginTransaction()
                        .replace(R.id.framelayout_left_pane, it, TRANSPORT_NAV_HOST_TAG)
                    .setPrimaryNavigationFragment(it)
                    .commit()
            }
        }
    }

运行应用程序的 devDebug 版本时,一切正常。我可以使用主 NavHostFragment 导航到不同的主从屏幕。在我导航到主从屏幕后,嵌套的 NavHostFragment 将接管,我可以使用嵌套的 NavHostFragment 将屏幕导航进出主详细信息片段。

当用户尝试单击后退按钮,这将导致离开主详细信息屏幕并导航到上一个屏幕时,我们会向用户弹出一个对话框,询问他们是否真的要离开屏幕(这是他们输入大量数据)。为了实现这一点,我们注册了一个 onBackPressDispatcher 回调,以便我们知道何时按下后退按钮并在调用回调时导航到对话框。在 devDebug 版本中,用户首先位于导航图上的位置 A。如果当他们在位置 A 时单击后退按钮,那么我们会显示一个对话框片段,询问用户是否真的打算离开屏幕。相反,如果用户从位置 A 导航到位置 B 并单击返回,则他们首先导航回位置 A。如果他们再次单击返回按钮,调用后按调度程序回调,然后显示对话框片段,询问他们是否真的打算离开位置 A。因此,似乎后退按钮会影响嵌套 NavHostFragment 的后退堆栈,直到嵌套 NavHostFragment 只剩下一个片段. 当只剩下一个片段并单击后退按钮时,将调用 onBackPressDisapatcher 回调。这正是所需的行为。但是,当我使用 Fragment Scenario 编写 Instrumentation 测试时,我尝试测试 ParentFragment 我发现背压行为不同。在测试中,我使用 Fragment Scenario 启动 ParentFragment,然后运行测试,在嵌套的 NavHostFragment 中进行导航。当我单击后退按钮时,我希望嵌套的导航主机片段会弹出它的堆栈。然而,

我在 NavHostFragment 中设置了一些断点,似乎在运行测试时,NavHostFragment 没有设置为拦截返回点击。它的 enableOnBackPressed() 方法总是在将标志设置为 false 的情况下调用。

我不明白测试设置导致这种行为的原因是什么。我认为导航主机片段会自行拦截返回点击,直到它的后台堆栈上只剩下一个片段,然后才会调用 onBackPressDispatcher 回调。

我是否误解了我应该如何测试这个?为什么按下后退按钮时会调用 onBackPressDispatcher 的回调。

4

1 回答 1

2

FragmentScenario源代码中可以看出,它目前(从 Fragment 1.2.1 开始)setPrimaryNavigationFragment()使用. 这意味着被测试的 Fragment 不会拦截后退按钮,因此它的子片段(例如 your NavHostFragment)不会拦截后退按钮。

您可以在测试中自己设置此标志:

@Test
fun testParentFragment() {
    // Use the reified Kotlin extension to launchFragmentInContainer
    with(launchFragmentInContainer<ParentFragment>()) {
        onFragment { fragment ->
            // Use the fragment-ktx commitNow Kotlin extension
            fragment.parentFragmentManager.commitNow {
                setPrimaryNavigationFragment(fragment)
            }
        }
        // Now you can proceed with your test
    }
于 2020-02-10T00:41:13.537 回答