Android Fragments 如何在多模块架构中重用导航图中的片段?

mpbci0fu  于 2023-03-08  发布在  Android
关注(0)|答案(1)|浏览(152)
    • bounty将在5天后过期**。回答此问题可获得+150声望奖励。4gus71n希望引起更多人关注此问题。
    • 背景**

我有一个多模块架构,有多个功能模块,看起来像这样:

我有多个依赖于:core_library库模块的功能模块,其中包含所有常见的依赖项(Retrofit、Room等),然后是针对每个不同应用流程的不同功能模块,最后是:app应用模块。
如果您想在功能模块中互不了解的Activity之间导航,我使用AppNavigator接口:

interface AppNavigator {
   fun provideActivityFromFeatureModuleA(context: Context): Intent
}

然后在:app模块Application类中实现这个接口,由于:app模块把所有东西都联系在一起,所以它知道每个特性模块中的每个活动:

class MyApp : Application(), AppNavigator {
...
   override fun provideActivityFromFeatureModuleA(context: Context): Intent {
      return Intent(context, ActivityFromA::class.java)
   }
...
}

AppNavigator组件位于:core_library中的Dagger模块中,并且可以注入到任何特性模块中。
我有这个:feature_login功能模块,当用户创建一个新帐户,必须通过入站流程,如邀请朋友加入应用程序,检查POST_NOTIFICATION权限,添加任何更多的细节到其帐户,等等。
每个:feature_modules都有一个Activity和许多Fragments我有一个导航图来在片段之间导航。

    • 问题是**

:feature_login导航图看起来像这样:

问题是,我需要在应用程序的不同部分重用这些片段中的许多片段,更具体地说,这些片段

例如:当我打开应用程序并进入主屏幕时,我会检查POST_NOTIFICATION权限,如果这些权限尚未被授予,我想提示PostNotificationFragment检查并向用户显示UI。如果用户想从"设置"屏幕更改它们,则应提示SelectSquadronFragment + SelectNumberFragment。在执行某些操作时,我希望使用InviteFriendsFragment提示用户。

  • 问题是我不知道如何独立地重用这些片段,而不让它们在流的其余部分中导航 *

"我所尝试的"

  • 子图并不能真正解决这个问题。我可以使用AppNavigator来提供我在:feature_login中的托管Activity或每个单独的Fragment,但问题仍然存在。如果用户从Settings打开SelectSquadronFragment + SelectNumberFragment,我不希望用户之后必须通过FinishFragment
  • 提取通过接口到Activity的导航。该导航图中的每个Fragment都通过NavDirections导航。当我想从MedictFragment导航到InviteFriendsFragment时,我使用MedicFragmentDirections。我在考虑让Activity提供这些NavDirections,这样我就可以创建具有所需导航路线的自定义Activity。但老实说,我更喜欢一些不那么高深的东西。

请让我知道,如果你需要我给你更多的信息。任何反馈是受欢迎的。

    • 示例**

让我给你们一个精确的例子来说明我在这里遇到的困难,让我们使用一些简单的东西,让我们以ChooseRoleFragment为例。
ChooseRoleFragment是一个简单的UI,在***登录流程***中显示了三个按钮和三个角色("警察"、"医生"和"消防员"),当用户点击这三个按钮中的一个时,他将被带到PoliceFragmentFiremanFragmentMedicFragment。这是在*登录流程中。
现在,我需要在应用程序的"设置"部分重新使用这个ChooseRoleFragment。唯一的区别是,当我在那里使用它时,我不希望它导航到FiremanFragmentMedicFragmentPoliceFragment,我只想让它回到"设置"屏幕。"设置"屏幕在一个完全不同的功能模块上,不"我对:feature_login一无所知
更清楚地说,ChooseRoleFragment通过导航图导航到PoliceFragmentFiremanFragmentMedicFragment,这意味着在ChooseRoleFragment中,当我单击每个选项时,我会看到如下内容:

class ChooseRoleFragment : Fragment() {
        //...
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            bindings.policeBtn.setOnClickListener {
                findNavController().navigate(ChooseRoleFragmentDirections.actionChooseRoleFragmentToPoliceFragment())
            }
            bindings.medicBtn.setOnClickListener {
                findNavController().navigate(ChooseRoleFragmentDirections.actionChooseRoleFragmentToMedicFragment())
            }
            bindings.firemanBtn.setOnClickListener {
                findNavController().navigate(ChooseRoleFragmentDirections.actionChooseRoleFragmentToFiremanFragment())
            }
        }
    }

所以,这对于登录流程非常有效,但是对于"Settings"屏幕,它不能完成我想要的功能。所以我想弄清楚的是,我应该在某个地方提取这些NavDirections吗?这样,当我想在"Settings"屏幕中重用这个Fragment时,我可以覆盖NavDirections,让它导航到其他地方。

1tu0hz3e

1tu0hz3e1#

您的目标是将不同的接口实现传递到ViewModel的构造函数中,以便当Fragment访问时,它会更改您要访问的目的地。您还希望提取这些来自外部的调用findNavController().navigate(ChooseRoleFragmentDirections.actionChooseRoleFragmentToPoliceFragment()),而不是直接硬编码操作必须执行的操作。此外,如果这些Fragment被重用,它们不能拥有自己的操作。这些动作必须来自其封闭的NavGraph。
那么,如何根据当前的应用程序状态将不同的接口实现放入Fragment或ViewModel中呢?
问得好,因为ViewModel(它将<T extends ViewModel>硬编码为预期类型)和Hilt(全局配置)都不是为此设计的。如果你坚持使用Jetpack Navigation,我可以设想两种选择(你就是这样,因为你的应用很大):
1.)在Fragment中定义一个接口为“ActionHandler”,并期望ActionHandler实现的FQN作为fragment参数,然后使用Class.forName(FQN).newInstance()获取对它的引用,这将定义Fragment必须执行的导航动作。
2.a)在ViewModel中定义接口为“ActionHandler”,并期望ActionHandler作为构造函数参数。根据当前可用的NavGraph,从定制ViewModelProvider.Factory(可能是initializerViewModel {)传入不同的ActionHandler实现。然而,这需要在您的定制ViewModelProvider.Factory中看到NavController。
2.b)合并这两种思想initializerViewModel {接收CreationExtras,包括关联的SavedStateHandle,因此您可以通过SavedStateHandle将类似“mode”的枚举(或之前的FQN)接收到ViewModelProvider.Factory中,并基于片段参数设置ActionHandler实现的条件。
这样,您可以为ActionHandler创建工厂,而不必在Fragment类中硬编码操作处理。如果您使用FQN,请确保使用@Keep注解或相关的Proguard规则来@Keep该类。此ActionHandler实现需要查看多个NavGraph,所以它基本上是:app中定义的某种Navigator,或者至少是一个可以看到多个NavGraph的模块。

相关问题