导航组件:找不到NavController

31

我正在使用导航组件来在我的应用程序中进行导航。它在片段内运行良好,但在持有实际导航主机的活动中无法找到nav主机。

当用户单击我包含在Main activity XML中的FAB时,我尝试打开一个新的fragment。当我调用findNavController()时,它无法找到控制器。nav host控制器位于XML布局中。我不明白为什么它无法找到它。

MainActivity

class MainActivity : AppCompatActivity(), OnActivityComponentRequest {
    override fun getTabLayout(): TabLayout {
        return this.tabLayout
    }

    override fun getFap(): FloatingActionButton {
        return this.floatingActionButton
    }

    private lateinit var tabLayout: TabLayout
    private lateinit var floatingActionButton: FloatingActionButton

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        this.tabLayout = tabs
        this.floatingActionButton = fab

        fab.setOnClickListener {

         it.findNavController().navigate(R.id.addNewWorkoutFragment)

        }
    }
}

主活动 XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".domain.MainActivity"
        android:animateLayoutChanges="true">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

        <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <com.google.android.material.tabs.TabItem
                    android:text="Test 1"
                    android:layout_height="match_parent"
                    android:layout_width="match_parent"/>

            <com.google.android.material.tabs.TabItem
                    android:text="Test 2"
                    android:layout_height="match_parent"
                    android:layout_width="match_parent"/>
        </com.google.android.material.tabs.TabLayout>

    </com.google.android.material.appbar.AppBarLayout>

      <fragment
        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:navGraph="@navigation/main_navigation" />


    <com.google.android.material.bottomappbar.BottomAppBar
            android:id="@+id/bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"/>


    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_anchorGravity="right|top"
            app:layout_anchor="@+id/bar"
            android:src="@drawable/ic_add_black_24dp"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

我不明白为什么它无法找到它。它是否返回“null”? - Anmol
是的,它返回null。我成功找到了一个解决方案。我会将其作为答案发布,这样更易读。 - Jeremi
1
Navigation.findNavController( this, R.id.nav_host) .navigate(R.id.addNewWorkoutFragment)。尝试使用这个,应该可以工作。 - Anmol
我也尝试了,但是没有成功。我已经发布了我的解决方案。 - Jeremi
你能否尝试在活动的onStart()中设置OnClickListener并检查一下。你的解决方案似乎有些hack。 - Anmol
显示剩余4条评论
12个回答

26

在我找到这种方式获取navController之前,我一直遇到同样的异常:

val navHostFragment = supportFragmentManager.findFragmentById(R.id.my_fragment_container_view_id) as NavHostFragment
val navController = navHostFragment.navController

以下是从这个答案中获取的:

如果您正在使用绑定,请在查找导航控制器之前设置视图:

binding = BaseLayoutBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)

如果不是:

setContentView(R.layout.activity_main)

使用相应的绑定来处理片段。


2
这实际上是导航组件文档中所述的内容:https://developer.android.com/guide/navigation/navigation-getting-started#navigate“使用FragmentContainerView创建NavHostFragment或通过FragmentTransaction手动将NavHostFragment添加到活动中时,在Activity的onCreate()中尝试通过Navigation.findNavController(Activity,@IdRes int)检索NavController将失败。您应该直接从NavHostFragment中检索NavController。” - Socram
1
在查找导航控制器之前,您需要设置视图。这是帮助我的方法。 - lasec0203

18

在Java中可以通过以下方式在活动中找到NavController

Navigation.findNavController(this,R.id.nav_host).navigate(R.id.YourFragment);

17

尝试在活动的onStart中设置浮动操作按钮(Fab)的onClick监听器,就像在onCreate活动中一样,只是膨胀视图并没有设置NavHostController。因此,如果您在活动的onStart中设置onClickListener,它将按预期工作。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    this.tabLayout = tabs
    this.floatingActionButton = fab
  }

override fun onStart() {
    super.onStart()
    floatingActionButton.setOnClickListener {
      it.findNavController().navigate(R.id.addNewWorkoutFragment)
    }
  }

现在SOF有一个感谢功能,可以在答案旁边使用。 :P 无论如何,你是受欢迎的@KhamidjonKhamidov - Anmol
是的,在onCreate()中它崩溃了,但在onStart()中它正常工作。 - Mehmet Gür
1
奇怪的是,我在一个新的“底部导航活动”项目中遇到了同样的问题。将导航控制器逻辑移动到onStart()中就可以解决这个问题! - François Noël

4

稍后在onPostCreate中检索它,就像这样:

@Override
public void onPostCreate(Bundle savedInstanceState ) {
    super.onPostCreate(savedInstanceState);
    
    navController = Navigation.findNavController(this, R.id.fragment_container_view);        
}

3
仅限 Kotlin: Android Navigation KTX 提供了 Activity 的扩展函数。
我所需要做的就是:
findNavController(R.id.myNavHostFragment).navigate(R.id.to_someFragment)

我在我的app/gradle.build.kts文件中有以下依赖项()。
implementation ("androidx.navigation:navigation-fragment-ktx:2.3.5")
implementation ("androidx.navigation:navigation-runtime-ktx:2.3.5")
implementation ("androidx.navigation:navigation-ui-ktx:2.3.5")

使用说明:
您也可以使用生成的 Directions 类。由于这是全局操作,因此 Directions 类位于 NavigationDirections.to_someFragment() 中。
//very useful if you're passing args then you could do something like
//NavigationDirections.to_someFragment(myArg)    
NavigationDirections.to_someFragment()findNavController(R.id.myNavHostFragment).navigate(NavigationDirections.to_someFragment())

附注:特定的扩展函数是从runtime-ktx aar中贡献的。

3
问题可能是FAB被添加到与NavHost片段不同的活动或片段中。在这种情况下,当您调用it.findNavController()时,它无法找到导航控制器。
您可以检查您的FAB是否属于NavHost引用的片段,或者调用Activity的findNavController(<id>)并传递要查找的片段的ID。
override fun onCreate(savedInstanceState: Bundle?) {
        ...
        fab.setOnClickListener {
            findNavController(R.id.nav_host_fragment)
                .navigate(R.id.addNewWorkoutFragment)

        }
    }

2
原来持有导航控制器的活动并没有导航组件。
解决方案是手动为包含在活动中的每个视图设置NavController。
val navController = findNavController(R.id.nav_host_fragment)
Navigation.setViewNavController(fab, navController)

现在这样就可以了:
fab.setOnClickListener {

    it.findNavController().navigate(R.id.addNewWorkoutFragment)

}

我仍然不明白为什么它能够按照这种方式工作,欢迎提供任何解释 :)

目前为止,Android API 简直毫无意义。

来源:Navigate to fragment on FAB click (Navigation Architecture Components)


1

这是Kotlin的扩展版本:

 fun AppCompatActivity.findNavController(
    @IdRes navHostViewId: Int
): NavController {
    val navHostFragment =
        supportFragmentManager.findFragmentById(navHostViewId) as NavHostFragment

    return navHostFragment.navController
}

navHostViewId 是你的 androidx.fragment.app.FragmentContainerView 的id。


1

您可以使用 binding.root.findNavController().navigate(id)



1
我发现这个答案很有帮助。在你的活动中粘贴这段代码。
Navigation.findNavController(this, R.id.nav_host_fragment).navigate(R.id.yourDestination)

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