导航组件:找不到 NavController

Navigation Component: Cannot find NavController

我正在使用导航组件在我的应用程序中导航。它在片段内工作正常,但无法在包含实际导航主机的 activity 中找到导航主机。

我试图在用户单击 FAB 时打开一个新片段,我将其包含在 Main activity 的 XML 中。当我调用 findNavController() 时,它找不到控制器。导航主机控制器位于 XML 布局中。我不明白为什么它找不到它。

主要Activity

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)

        }
    }
}

Activity主要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>

原来 activity 包含导航控制器...没有导航组件。

解决方案是手动将 NavController 设置为 activity 中包含的每个视图。

val navController = findNavController(R.id.nav_host_fragment)
Navigation.setViewNavController(fab, navController)

现在这行得通了:

fab.setOnClickListener {

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

}

我还是不明白为什么会这样,所以欢迎任何解释:)

到目前为止,Android API 根本没有多大意义。

来源:

尝试在 Activity 的 onStart 中设置 Fab 按钮的 onClickListener,因为在 onCreate 中 Activity 只是膨胀视图而没有设置 NavHostController。因此,如果您在 activity 的 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)
    }
  }

问题可能是 FAB 添加到 activity 或与 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)

        }
    }

在 Java 中,您可以这样在 activity 中找到 NavController

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

您仍然可以通过执行以下操作在您的 oncreate 中获得访问权限

    //the id would be the id of the Fragment Element In Your Activity XML File
    val navGraph = Navigation.findNavController(this, R.id.activity_main)
    binding.mainFab.setOnClickListener {
        navGraph.navigate(R.id.destinationFragment)
    }

在我设法以这种方式获得 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)

对片段使用相应的绑定。

我觉得这个回答很有帮助。在您的 activity 中粘贴此代码

Navigation.findNavController(this, R.id.nav_host_fragment).navigate(R.id.yourDestination)

这是 Kotlin 的扩展 版本:

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

    return navHostFragment.navController
}

navHostViewId 是您的 androidx.fragment.app.FragmentContainerView 的 ID。

在Activity的OnCreate中访问navController的最佳方式是在inflating布局之后访问它 可以通过 kotlin 扩展函数 doOnPreDraw()

实现
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private val navController: NavController
        get() = Navigation.findNavController(this, R.id.nav_host_fragment)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.root.doOnPreDraw {
            // here you can safely use navController
            binding.toolbar.setupWithNavController(navController)
        }
    }
}

仅 Kotlin

Android Navigation KTXActivity

提供扩展函数

我所要做的就是

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 class。由于这是一项全球行动,因此 Directions class 位于 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

贡献的