Android导航组件:回击导致NavController不同步
我正在将一个应用程序从通过Android导航组件:回击导致NavController不同步,android,android-fragments,android-jetpack-navigation,Android,Android Fragments,Android Jetpack Navigation,我正在将一个应用程序从通过FragmentManager手动后台处理迁移到使用新的导航库。该应用程序由一个main活动组成,该活动有一个DrawerLayout,抽屉中有10-12个项目。当点击时,会发生一些片段操作,并显示一个新片段 在这个生产应用程序中有足够的代码,因此简单地删除旧的导航结构并将其整体移动到导航库是不可行的 相反,我所做的是逐抽屉项迁移抽屉项。例如,有一个叫做“支票存款”的功能,可以通过导航抽屉中的项目进行访问。当您点击该项目时,会显示一个新片段,您可以通过支票存款向导(如f
FragmentManager
手动后台处理迁移到使用新的导航库。该应用程序由一个main活动
组成,该活动有一个DrawerLayout
,抽屉中有10-12个项目。当点击时,会发生一些片段操作,并显示一个新片段
在这个生产应用程序中有足够的代码,因此简单地删除旧的导航结构并将其整体移动到导航库是不可行的
相反,我所做的是逐抽屉项迁移抽屉项。例如,有一个叫做“支票存款”的功能,可以通过导航抽屉中的项目进行访问。当您点击该项目时,会显示一个新片段,您可以通过支票存款向导(如flow)前进。到目前为止,我已经创建了一个新的RootCheckDeposit
片段,它的视图
由一个包含导航图的FragmentContainerView
组成。然后,支票存款向导的整个“流程”在导航图中导航,导航图最终包含在RootcheckDeposit
中
下面是该RootCheckDeposit
片段的布局:
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/check_deposit_graph" />
需要注意的是,当我们从加载屏幕
转到术语屏幕
到登录屏幕
时,我们会弹出每个项目,因为我不希望用户能够“返回”加载屏幕或术语屏幕
我尝试过的事情:
- 将导航图中的
popup逻辑更改为指向实际导航图而不是目的地
- 检查
的结果是否为navController.popbackbackstack
或true
,并采取相应的措施false
NavController
代码,似乎正在发生的是,NavController
在单击“后退”时会弹出其内部后退,但不会更改目的地,因为实际上没有其他目的地可去。然后,NavController
似乎处于一种奇怪的不一致状态,它和当前显示的Fragment
不正常/不指向同一事物
还有其他人成功地完成了这种迁移吗
编辑:我已经创建了一个回购协议,它有一个最小的项目来重新创建我遇到的崩溃:。这种回购类似于我正在开发的生产应用程序的结构。如果您点击幻灯片抽屉项目,然后继续点击屏幕中的文本,直到进入下一个示例片段,然后回击几次,再次点击文本,您将看到崩溃
我确信问题在于我如何设置导航库,我只是不确定正确的方法是什么,而不重写其余的应用程序导航,根据:
在任何给定的时间,只允许一个FragmentManager控制片段回栈。如果您的应用程序同时在屏幕上显示多个兄弟片段,或者如果您的应用程序使用子片段,则必须指定一个FragmentManager来处理应用程序的主导航
要在片段事务内部定义主导航片段,请调用该事务的方法,传入其childFragmentManager应具有主控制权的片段实例
如中所述,app:defaultNavHost=“true”
正在NavHostFragment
本身上为您调用setPrimaryNavigationFragment()
,但您没有在父片段上调用setPrimaryNavigationFragment()
。这意味着正在调用自动正确处理后退按钮的逻辑的none——实际上,您已经断开了从活动到父片段再到NavHostFragment
的链。这会导致您在没有后台堆栈的情况下拦截“后退”按钮(当您弹出最后一个片段时,导航实际上不会删除它,否则在活动的退出动画运行时它将不存在)
因此,要解决此问题,您需要:
RootSlideshowFragment
中删除您的OnBackPressedCallback
。这是不必要的main活动
中的片段之间交换时,对新片段调用setPrimaryNavigationFragment()
:“如果你回击什么都没发生-这很好”-不,那不好,这是一个你回击不正确的信号。您是否正在重写onBackPressed()并手动调用
popBackStack()
?如果您已正确设置了片段层次结构,则不需要这样做。抱歉,我无法重现此错误。我使用了你的导航图和很多几乎是空的片段-一切都正常工作(特别是从LandingFragment向后按,允许我返回到CheckDepositorRoot片段之前的片段)。如果你需要更多的帮助,请考虑与问题分享behavior@ianhanniballake你说得对——我的感觉更像是“我对那种行为没意见”。我非常愿意接受这样一种观点,即我错误地设置了片段层次结构,我只是不确定正确的设置方式是什么。本质上,我不太明白当你点击导航堆栈中最后一个片段的“back”时会发生什么。什么都不应该发生吗?整个图表应该弹出吗?我不清楚。@BömachtBlau当你说“从LandingFragment返回允许我返回到CheckDepositorRootFragment之前的片段”时,在CheckDepositorRootFragment之前是什么?在我的应用程序中,这是导航堆栈的根,因此除非您更改导航抽屉项目(或退出应用程序),否则没有任何地方可以真正“返回”。我会试着做个mi
<?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/check_deposit_graph"
app:startDestination="@id/loadingFragment">
<fragment
android:id="@+id/loadingFragment"
android:name="module.checkdeposit.loading.TermsLoadingFragment"
android:label="@string/check_deposit_title"
tools:layout="@layout/terms_loading_fragment" >
<action
android:id="@+id/action_loadingFragment_to_termsAndConditionsFragment"
app:destination="@id/termsAndConditionsFragment"
app:popUpTo="@id/loadingFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_loadingFragment_to_depositOptionsFragment"
app:destination="@id/depositLandingFragment"
app:popUpTo="@id/loadingFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/termsAndConditionsFragment"
android:name="module.checkdeposit.terms.TermsAndConditionsFragment"
android:label="@string/check_deposit_terms_and_conditions"
tools:layout="@layout/terms_and_conditions_fragment" >
<action
android:id="@+id/action_termsAndConditionsFragment_to_depositLandingFragment"
app:destination="@id/depositLandingFragment"
app:popUpTo="@id/termsAndConditionsFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/depositLandingFragment"
android:name="module.checkdeposit.depositlanding.DepositLandingFragment"
android:label="@string/check_deposit_title"
tools:layout="@layout/deposit_landing_fragment" >
<action
android:id="@+id/action_depositLandingFragment_to_depositHistoryFragment"
app:destination="@id/depositHistoryFragment" />
<action
android:id="@+id/action_depositLandingFragment_to_scanCheckFragment"
app:destination="@id/scanCheckFragment" />
</fragment>
<fragment
android:id="@+id/depositHistoryFragment"
android:name="module.checkdeposit.deposithistory.DepositHistoryFragment"
android:label="@string/check_deposit_deposit_history"
tools:layout="@layout/deposit_history_fragment" />
<fragment
android:id="@+id/scanCheckFragment"
android:name="module.checkdeposit.scancheck.ScanCheckFragment"
android:label=""
tools:layout="@layout/scan_check_fragment" />
</navigation>
navView.setNavigationItemSelectedListener { menuitem ->
val newFragment = when (menuitem.itemId) {
R.id.nav_home -> HomeFragment()
R.id.nav_gallery-> GalleryFragment()
else -> RootSlideshowFragment()
}
supportFragmentManager.beginTransaction()
.replace(R.id.container, newFragment)
.setPrimaryNavigationFragment(newFragment)
.commit()
true
}