导航架构组件- 将参数数据传递到startDestination

99

我有一个启动活动A,通过意图数据传递给活动B。活动B托管了一个新的导航架构组件导航图。我想将该意图数据作为参数传递到startDestination片段中,如何实现?


我相信导航编辑器有“参数”的选项,然后您可以添加safe-args gradle插件,但是细节对我来说有点模糊。 - EpicPandaForce
2
是的,我知道,但是没有办法将参数传递到起始目标片段。 - Ahmed Abdelmeged
12个回答

46

好的,我通过来自Google团队的Ian Lake的帮助找到了解决那个问题的方法。

假设你有一个将使用某些意图数据启动活动B的活动A,并且你想在startDestination中获取该数据,这里有两个选择,如果你使用安全参数(即我的情况),你可以使用

StartFragmentArgs.fromBundle(requireActivity().intent?.extras)

从Intent中读取args。如果你不使用安全参数,你可以使用requireActivity().intent?.extras手动从bundle中提取数据,它会返回一个Bundle,你可以使用它代替片段的getArguments()方法。我尝试了一下,一切正常运行。


1
StartFragmentArgs 类从哪里来?它是否包含在导航架构依赖项中? - sam33r
1
这是导航生成的类。如果您正在使用安全参数,例如此处的片段名为StartFragment,则生成的arg类将是StartFragmentArgs。如果片段名为FooFragment,则生成的类将是FooFragmentArgs,以此类推。 - Ahmed Abdelmeged
1
当您启动具有图表的活动时,将数据作为意图传递并在requireActivity().intent?.extras中提取。 - Ahmed Abdelmeged
你需要使用导航安全参数Gradle插件。 - Ahmed Abdelmeged
3
你的答案不完整。当你使用SafeArgs设置bundle时,它会返回类对象本身,设置所有的bundle参数。但是你没有告诉我们如何在fragment中获取回那个bundle。如果我们使用fromBundle,它总是会返回一个新的对象,所以当你尝试获取它时,它将不再具有该值,并且将为null。 - Cyph3rCod3r
显示剩余4条评论

41

我认为这在1.0.0版本中又发生了改变。而且谷歌已经将这个信息在官方文档中隐藏得很好。或者至少我挣扎着去找到它,但是在《迁移到导航组件》指南中偶然发现了它。如何将参数传递给起始目标在这里提到:将活动目标参数传递到起始目标片段

简而言之

  1. 您必须以编程方式设置导航图:

findNavController(R.id.main_content)
    .setGraph(R.navigation.product_detail_graph, intent.extras)
  1. 不要在NavHostFragment的XML声明中设置图表。
  2. 从接收方读取extras:
val args by navArgs<ProductDetailsArgs>()  
val productId = args.productId
更新:Google表示,传递参数给初始导航目标的官方文档确实遗漏了。希望这很快会成为导航组件文档的一部分。

嗨@Johan Paul,你能告诉我这里的R.id.main_content是什么吗? - rupesh
@rupesh 是 NavHostFragment 的 ID,在 XML 布局文件中通常会在此处指定导航图。 - Johan Paul

38

TLDR:您需要手动填充图表,将键/值添加到defaultArgs中,并将图表设置为navController

步骤1

文档告诉您在Activity的布局中的<fragment>标记中设置图表。类似于以下内容:

<fragment
    android:id="@+id/navFragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:graph="@navigation/nav_whatever"
    app:defaultNavHost="true"
    />

移除设定graph=的那一行。

步骤2

在将显示您的NavHostFragment的 Activity 中引入图表,如下所示:

val navHostFragment = navFragment as NavHostFragment
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_whatever)

其中 navFragment 是你在 XML 中为 fragment 指定的 id。

第三步 [关键步骤!]

创建一个 Bundle 来保存你想要传递给 startDestination fragment 的参数,并将其添加到图形的默认参数中:

val bundle = Bundle()
// ...add keys and values
graph.addDefaultArguments(bundle)

第四步

将图表设置在主机的navController上:

navHostFragment.navController.graph = graph

你的解决方案可以工作,但相当复杂。我发现了更好的方法,我会在另一个答案中提供。 - Ahmed Abdelmeged
27
这个解决方案在1.0.0-alpha09中已经不再可用。addDefaultArgument函数已被移除。 - Boonya Kitpitak
addDefaultArgument已从Navigation Architecture Component中移除。请查看下面的答案,以了解官方推荐的方法。 - Praveen Singh

32

这个问题已经在1.0.0-alpha07版本中得到了修复。 查看详细信息。

这个解决方案类似于Elliot Schrock的答案,但是通过官方API进行封装。

我们需要手动填充NavHostFragmentgraph

使用

NavHostFragment.create(R.navigation.graph, args)

或者

navController.setGraph(R.navigation.graph, args)

args 是我们要传递给起始目的地的数据。


21

根据官方文档中的“将数据传递到起始目的地”部分:

首先,构建一个包含数据的 Bundle:

val bundle = Bundle().apply {
    putString(KEY, "value")
}

接下来,使用以下方法之一将Bundle传递给起始目的地:

  • 如果您正在通过编程方式创建NavHost

     NavHostFragment.create(R.navigation.graph, bundle)
    
    否则,您可以通过调用下列任一NavController.setGraph()的重载函数之一来设置起始目标参数:
  •  navHostFragment.navController.setGraph(R.navigation.graph, bundle)
    

然后您应该使用Fragment.getArguments()在起始目的地中检索数据。

编辑:

您还可以使用FragmentArgs而不是手动创建bundle,这使其更加方便和类型安全:

navHostFragment.navController.setGraph(R.navigation.graph, MyFragmentArgs(arg).toBundle())

那么在片段中,您可以检索args:

private val args: PodFragmentArgs by navArgs()

确保在navigation.xml文件中,您的片段具有argument元素:

<fragment
        android:id="@+id/myFragment"
        android:name="MyFragment"
        android:label="fragment_my"
        tools:layout="@layout/fragment_my">
        <argument
            android:name="argName"
            android:defaultValue="@null"
            app:argType="string"
            app:nullable="true" />
</fragment>

链接已损坏。如果您记得,请更新链接。 - Pankaj Kumar
1
@PankajKumar 链接已更新,感谢您告诉我。 - Nijat Ahmadli

14

最后,我找到了解决这个问题的方法。

在撰写本答案时,我正在使用Navigation 2.2.0-alpha01

如果您想直接将一些数据作为参数从宿主活动传递到起始目的地,则需要在宿主活动的onCreate()方法中手动设置您的宿主导航图,如下所示:

获取您的navController:

val navController by lazy { findNavController(R.id.<your_nav_host_id>) }

然后在主活动的onCreate()方法中

val bundle = Bundle()
bundle.putString("some_argument", "some_value")
navController.setGraph(R.navigation.<you_nav_graph_xml>, bundle)

或者,如果您希望将整个意图额外内容原封不动地传递给startDestination:

navController.setGraph(R.navigation.<you_nav_graph_xml>, intent.extras)

intent.extras只会返回一个Bundle

当你使用setGraph()方法设置时,应避免在定义中设置属性,因为这样会导致导航图被实例化和设置两次。

如果您正在读取起始目标片段中的这些参数:

如果您正在使用安全参数插件(强烈推荐),那么在片段中:

private val args by navArgs<DummyFragmentArgs>()

Safe Args插件会通过在片段名称末尾添加Args来生成一个Args类。例如,如果您的片段名为DummyFragment,那么Safe Args将生成一个名为DummyFragmentArgs的类。

其中navArgs<>是在Android KTX中定义的扩展函数。

如果您不使用Android KTX,则可以通过以下方式获取args对象:

val args = DummyFragmentArgs.fromBundle(arguments!!)

一旦获取了arguments对象,您可以简单地获取您的参数:

args.someArgument

请注意我们如何将"some_argument"作为参数传递,并使用Safe Args将其读取为someArgument

如果您不使用Safe Args(尽管没有理由不使用),可以按以下方式访问您的参数:

arguments?.getString("some_argument")

这一切都在“迁移到导航组件”文档中有详细说明,文档链接如下:https://developer.android.com/guide/navigation/navigation-migrate#pass_activity_destination_args_to_a_start_destination_fragment


你能用Java给出这个解决方案吗? - Adnan haider
setGraph方法不知何故无法工作。 - Raghav Satyadev

2
阅读了解决方案后,我制作了一个适合我的需求的解决方案。这个解决方案假定发送给托管此图表的活动的数据在起始目标上。
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // work around get get starting destination with activity bundle
    userId = getActivity().getIntent().getIntExtra(KEY_USER_ID, -1);
}

2

在最新版本的库中,addDefaultArguments已经不存在了。我已经通过以下方式解决了这个问题:

        val navHostFragment = fragment_navigation_onboarding as NavHostFragment
        val navController = navHostFragment.navController
        val navInflater = navController.navInflater
        val graph:NavGraph = navInflater.inflate(R.navigation.navigation_your_xml)
        val model = Model()//you might get it from another activity
        graph.addArgument("Data", NavArgument.Builder().setDefaultValue(model).build()) // This is where you pass the bundle data from Activity to StartDestination
        navHostFragment.navController.graph = graph

1
你可以将数据传递给应用程序的起始目标。首先,您必须明确构建一个包含数据的Bundle。接下来,使用以下方法之一将Bundle传递到起始目标 您可以手动从代码设置图形,并添加参数,从xml中删除app:navGraph,并在活动中使用此行代码。
navController.setGraph(R.navigation.graph, args)

1

对于仍然在努力解决这个问题的人们,我发现了另一种方法,可以不使用Safe-Args并且不需要使用@Elliot的答案中的步骤。

假设你从Activity A接收到了一些参数,并且你的Activity B有一个fragment startDestination,你可以像下面这样初始化Nav控制器:

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

从 Nav Controller 中,您可以访问在 XML 中设置的图形,并且可以在 defaultArguments 中设置参数:
navController.getGraph().addDefaultArguments(extras);

注意:如果图形XML中已经存在键的值,则此操作还将更新键的值。
现在,在您的片段中,您必须像这样从NavHostFragment中找到默认参数:
Bundle defaultArguments = NavHostFragment.findNavController(this).getGraph().getDefaultArguments();

你将在那里拥有这些值。我不知道为什么@Elliot认为这很重要,但应该是这样的吗?

更新alpha09: 在此版本中不再支持addDefault参数,您必须使用NavArgument。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接