导航组件:找不到 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 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 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
贡献的
我正在使用导航组件在我的应用程序中导航。它在片段内工作正常,但无法在包含实际导航主机的 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 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 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