如何使用底部导航视图和 Android 导航组件将参数传递给片段?

Posted

技术标签:

【中文标题】如何使用底部导航视图和 Android 导航组件将参数传递给片段?【英文标题】:How to pass arguments to a fragment using bottom navigation view and Android Navigation component? 【发布时间】:2019-06-02 13:39:51 【问题描述】:

是否可以使用底部导航视图和导航组件在片段中传递和访问参数?

我正在使用具有多个片段的单一活动方法,其中我的***片段需要一个参数(通常通过 newInstance 生成的方法完成)。我查看了导航组件开发人员指南和代码实验室,但它只提到使用安全参数并在目的地和操作中添加参数标签。

这是我的导航图:

<navigation xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" 
    app:startDestination="@id/homeFragment">

    <fragment android:id="@+id/homeFragment"
          android:name="uk.co.homeready.homeready.HomeFragment"
          android:label="fragment_home"
          tools:layout="@layout/fragment_home">
          <!--Do I create an argument block here?-->
    </fragment>

    <fragment android:id="@+id/calculatorFragment"
          android:name="uk.co.homeready.homeready.CalculatorFragment"
          android:label="fragment_calculator"
          tools:layout="@layout/fragment_calculator"/>

    <fragment android:id="@+id/resourcesFragment"
          android:name="uk.co.homeready.homeready.ResourcesFragment"
          android:label="fragment_resources"
          tools:layout="@layout/fragment_resources"/>

</navigation>

底部导航视图菜单:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/homeFragment"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home"/>

    <item
        android:id="@+id/calculatorFragment"
        android:icon="@drawable/ic_baseline_attach_money_24px"
        android:title="@string/title_calculator"/>

    <item
        android:id="@+id/resourcesFragment"
        android:icon="@drawable/ic_baseline_library_books_24px"
        android:title="@string/title_resources"/>

</menu>

主活动:

override fun onCreate(savedInstanceState: Bundle?) 
        ...
        val navController = Navigation.findNavController(this, 
        R.id.nav_host_fragment)
        bottom_navigation.setupWithNavController(navController)
        ....

activity_main.xml

<android.support.constraint.ConstraintLayout>
    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"/>

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        app:menu="@menu/bottom_navigation"/>

</android.support.constraint.ConstraintLayout>

首页片段

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 
    val argument = //TODO access argument here
    ...

【问题讨论】:

所以你希望你的homeFragment 在你从底部导航点击它时以一些默认的参数集开始?您是在其他地方重用 homeFragment 并使用不同的参数,还是它们总是只是固定值? 是的,对于我的用例,我想在您点击底部导航时传递一些默认设置参数。我也可能在其他地方重用 homeFragment。 【参考方案1】:

如果我理解正确,您希望将参数传递给与菜单项相关的目的地。尝试在您的活动 onCreate 方法中使用“OnDestinationChangedListener”,如下所示:

navController.addOnDestinationChangedListener  controller, destination, arguments ->
        when(destination.id) 
            R.id.homeFragment -> 
                val argument = NavArgument.Builder().setDefaultValue(6).build()
                destination.addArgument("Argument", argument)
            
        
    

更新:

如果您希望您的起始目的地接收默认参数,则实现应该不同。 首先,从 'NavHostFragment' xml 标记中删除 'app:navGraph="@navigation/nav_graph"'。

然后,在您的活动 onCreate 中,您需要对图表进行充气:

 val navInflater = navController.navInflater
 val graph = navInflater.inflate(R.navigation.nav_graph)

然后将您的参数添加到图形(此参数将附加到起始目的地)

val navArgument1=NavArgument.Builder().setDefaultValue(1).build()           
val navArgument2=NavArgument.Builder().setDefaultValue("Hello").build()
graph.addArgument("Key1",navArgument1)
graph.addArgument("Key2",navArgument2)

然后将图表附加到 NavController:

navController.graph=graph

现在您的第一个目的地应该收到附加的参数。

【讨论】:

感谢亚历克斯的回答。我正在通过val argument = arguments?.getString("Argument") 检索 HomeFragment 中的参数但是在应用程序启动时 argument 在 HomeFragment 中为空,即使我可以看到正在调用侦听器并且正在主活动中创建参数。我必须点击底部导航中的另一个项目,然后点击并导航回 HomeFragment,以便 argument 返回一些内容 OnDestinationChangedListener 只在第二次起作用的原因是因为它是在navigate() 调用完成并且为第一个 Fragment 设置参数之后调用的。将&lt;argument&gt; 添加到图表中与在运行时手动创建NavArgument 完全相同,并且是推荐的方法。 我尝试了第一种方法,OnDestinationChangedListener 第一次没有工作。 @ianhanniballake 我遇到了同样的问题,OnDestinationChangedListener 第一次不起作用。我也通过 xml 将我的参数添加到了目的地,但它仍然不起作用。关于为什么的任何想法?【参考方案2】:

这样做的正确方法确实是在您的目的地上使用&lt;argument&gt; 块。

<fragment android:id="@+id/homeFragment"
      android:name="uk.co.homeready.homeready.HomeFragment"
      android:label="fragment_home"
      tools:layout="@layout/fragment_home">
      <argument
          android:name="Argument"
          android:defaultValue="value"
          />
</fragment>

这将使用默认值自动填充 Fragment 的参数,而无需任何额外的代码。从 Navigation 1.0.0-alpha09 开始,无论您是否使用 Safe Args Gradle 插件都是如此。

【讨论】:

如何在目标片段中接收这些参数? @NaumanAsh - the documentation 介绍了您如何接收参数,无论是否通过 Safe Args @ianhanniballake 当从bottomNavView 中点击一个项目时,我们能否以编程方式更改参数值?【参考方案3】:

默认值对我来说不可用,因为我有动态菜单项,其中可能有多个具有不同参数的相同目的地。 (从服务器更改)

实现BottomNavigationView.OnNavigationItemSelectedListener:

override fun onNavigationItemSelected(item: MenuItem): Boolean 
    val fragmentId = item.itemId
    val arguments = argumentsByFragmentId[fragmentId] // custom mutableMapOf<Int, Bundle?>() with arguments
    navController().navigate(fragmentId, arguments)
    return true

要使用它,您将通过替换侦听器来接管导航。这里的调用顺序很重要:

bottomNavigationView.setupWithNavController(navController)
bottomNavigationView.setOnNavigationItemSelectedListener(this)

【讨论】:

以上是关于如何使用底部导航视图和 Android 导航组件将参数传递给片段?的主要内容,如果未能解决你的问题,请参考以下文章

如何将加载覆盖进度条覆盖到android的“底部导航视图”(使用约束布局)

如何使用嵌套的 navGraph 和底部导航视图进行导航

如何使用嵌套的navGraph和底部导航视图导航

如何保存底部导航片段的状态 - 具有单个导航图的 Android 导航组件

如何在android中增加底部导航视图的高度及其图标和文本大小?

如何在android中使用底部导航视图时恢复片段状态?