【问题标题】:Navigation Component: Cannot find NavController导航组件:找不到 NavController
【发布时间】:2019-05-22 23:18:47
【问题描述】:

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

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

主活动

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>

【问题讨论】:

  • I can't understand why it fails to find it. 是否返回null
  • 是的,它返回空值。我设法找到了解决方案。我会把它作为答案,所以它更具可读性
  • Navigation.findNavController( this, R.id.nav_host) .navigate(R.id.addNewWorkoutFragment) 尝试使用它应该可以工作
  • 我也试过了,但是没用。我已经发布了我的解决方案。
  • 您可以尝试在活动的onStart() 中设置 OnClickListener 并检查。您的解决方案似乎是个 hack。

标签: android navigation android-jetpack


【解决方案1】:

在我设法以这种方式获取 navController 之前,我遇到了同样的异常:

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

从此answer获得:

在找到导航控制器之前,如果您使用绑定,则需要设置视图:

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

如果没有:

setContentView(R.layout.activity_main)

对片段使用相应的绑定。

【讨论】:

  • 这实际上是导航组件文档中所述:developer.android.com/guide/navigation/…“当使用 FragmentContainerView 创建 NavHostFragment 或通过 FragmentTransaction 手动将 NavHostFragment 添加到您的活动时,尝试在 onCreate( ) 通过 Navigation.findNavController(Activity, @IdRes int) 的 Activity 将失败。您应该直接从 NavHostFragment 检索 NavController。"
  • Before finding the nav controller, you need to set the view 这对我有帮助。
【解决方案2】:

尝试在onCreate 的Activity 的onStart 中设置Fab 按钮的onClickListener,就像在onCreate 中一样,Activity 只是在膨胀视图并且没有设置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 有thank's feature旁边回答你可以使用它。 :P 反正是你的厕所 @KhamidjonKhamidov
  • 是的,在 onCreate() 中它崩溃了,但在 onStart() 中它工作正常。
  • 奇怪的是,我在一个新的Bottom Navigation Activity 项目中遇到了同样的问题。将导航控制器逻辑移动到 onStart() 就像这样解决了问题!
【解决方案3】:

在 Java 中,您可以通过这种方式在活动中找到 NavController

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

【讨论】:

    【解决方案4】:

    原来拥有导航控制器的活动...没有导航组件。

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

    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)

    【讨论】:

      【解决方案5】:

      你仍然可以通过这样做在你的 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)
          }
      

      【讨论】:

        【解决方案6】:

        问题可能是 FAB 被添加到活动或与 NavHost 片段使用的片段不同的片段。在这种情况下,当您调用it.findNavController() 时,它找不到导航控制器。

        您可以检查您的 FAB 是否属于 NavHost 提取的片段,或者调用 Activity 的 findNavController(&lt;id&gt;) 并将您正在查找的片段的 id 传递给它

        override fun onCreate(savedInstanceState: Bundle?) {
                ...
                fab.setOnClickListener {
                    findNavController(R.id.nav_host_fragment)
                        .navigate(R.id.addNewWorkoutFragment)
        
                }
            }
        

        【讨论】:

          【解决方案7】:

          我发现这个答案很有帮助。在您的活动中粘贴此代码

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

          【讨论】:

            【解决方案8】:

            这是 Kotlin 的扩展版本:

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

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

            【讨论】:

              【解决方案9】:

              在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)
                      }
                  }
              }
              

              【讨论】:

                【解决方案10】:

                仅限 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 类。由于这是一个全局操作,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-ktxaar

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2020-03-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-08-08
                  • 2017-09-07
                  • 1970-01-01
                  • 2020-11-06
                  相关资源
                  最近更新 更多