<androidx.fragment.app.FragmentContainerView>和<fragment>作为NavHost的视图有什么区别?

44

当使用androidx.fragment.app.FragmentContainerView作为导航宿主(navHost)而非常规fragment时,在方向更改后应用程序无法导航到目的地。

我会收到以下错误消息: java.lang.IllegalStateException: no current navigation node

我是否存在应该知道的陷阱以正确使用它,或者我的导航组件使用方式不正确?

这是一个简单的带有视图的活动XML:


...
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:navGraph="@navigation/nav_simple" />
...

导航代码:

<?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/nav_legislator.xml"
    app:startDestination="@id/initialFragment">

    <fragment
        android:id="@+id/initialFragment"
        android:name="com.example.fragmenttag.InitialFragment"
        android:label="Initial Fragment"
        tools:layout="@layout/initial_fragment">
        <action
            android:id="@+id/action_initialFragment_to_destinationFragment"
            app:destination="@id/destinationFragment" />
    </fragment>
    <fragment
        android:id="@+id/destinationFragment"
        android:name="com.example.fragmenttag.DestinationFragment"
        android:label="Destination Fragment"
        tools:layout="@layout/destination_fragment" />

</navigation>

这里有一个 GitHub 存储库,您可以很容易地复现一个 bug:https://github.com/dmytroKarataiev/navHostBug


你需要将 <fragment> 放入 FragmentContainerView 中。 - EpicPandaForce
这对我很有帮助。 https://clopez27.com/blog/android-use-fragmentcontainerview-for-navigation - ikmazameti
5个回答

41
当你尝试调用 navigate() 方法时,如果没有图形集,则会出现“no current navigation node”错误。如果仅在使用 FragmentContainerView 且在配置更改后才发生,则与此错误相关,这将与此修复并计划发布 Navigation 2.2.0-rc03 的此错误有关。
为解决此问题,您可以选择切换回或移除 app:navGraph="@navigation/nav_simple" 并调用 navController.setGraph(R.navigation.nav_simple)。

2
我实际上尝试了这种方法,但是我得到了完全相同的错误。你能否检查一下代码库并尝试使用这种方法使其工作? - Dmytro Karataiev
谢谢,非常完美。 - Dmytro Karataiev
我遇到了一个 IllegalStateException 错误,提示我在调用 getGraph() 之前必须先调用 setGraph()。使用 <fragment> 标签解决了我的问题。谢谢! - G_comp
@G_comp - Navigation 2.2.0-rc03已经发布了,我强烈建议您再次尝试并确保它在稳定版发布之前被修复。 - ianhanniballake
1
这个应该在官方的代码实验室中有解释!或者是我错过了吗? - Robert
显示剩余4条评论

37
我不明白为什么要用FragmentContainerView替代Fragment视图。这就是为什么我添加了这个答案(下面有源代码)。

在活动中托管片段的常见模式之一是使用FrameLayout。 Android社区多年来一直在这样做,但现在这种情况将发生变化。 androidx团队引入了这个称为FragmentContainerView的新视图来为您完成此操作。

您所要做的就是将FrameLayout替换为FragmentContainerView,一切都应该开箱即用,您不需要更改任何关于处理片段事务的方式。

这里获得的好处是对片段的所谓z-ordering的处理得到了改进。这是他们使用的示例,但基本上这意味着例如两个片段之间的退出和进入转换不会重叠。相反,这个FragmentContainerView将首先启动退出动画,然后再启动进入动画。

如果您正在问这是否可以替换<fragment>标记?那么答案是肯定的。您所要做的就是在定义FragmentContainerView时添加android:name="your_class_name",它将在幕后使用FragmentTransaction来构建和显示您的片段。这意味着您可以稍后使用片段事务来替换它。

来源

Android文档


5
我不认为这个答案回答了作者所问的问题。 - AlgoRyan
谢谢!请参阅 https://dev59.com/nFIH5IYBdhLWcg3wWck0#64225308 以在proguard-rules.pro中添加规则。 - CoolMind
非常感谢您的回答。这对我现在面临的问题有所帮助,虽然并没有解决它。但是我更了解FrameLayoutFragmentContainerView之间的区别了。 - iam thadiyan
关于 android:name="your_class_name","your_class_name" 应该是托管 Activity 还是 Fragment? - ProjectDelta
原始问题是关于<fragment>而不是<FrameLayout>。 - alekop

0

我通过在活动类中的onCreate方法进行一些更改来解决我的问题。

如何处理Fragment(旧用法):

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        val navController = findNavController(R.id.nav_host_fragment_activity_main)
        ...
    }

如何处理 FragmentContainerView:
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment_activity_main) as NavHostFragment

        val navController = navHostFragment.navController
        ...
    }

-2
// drawer layout that I pass is the id of the layout in the activity from which I move to fragment//

SacnFragment sacnFragment = new SacnFragment();
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.drawer_layout, sacnFragment);
    transaction.addToBackStack(null);
    transaction.commit();

-2

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