126

我正在使用 Android 导航组件进行导航。我有一个 LoginFragment,它有一个转换为 SignUpFragment 的按钮。单击按钮时,我收到此错误。

java.lang.IllegalStateException: View android.support.v7.widget.AppCompatButton{49d9bd1 VFED..C.. ...P.... 201,917-782,1061 #7f090172 app:id/signUpLink} does not have a NavController set

这是我的 nav_graph.xml

<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        app:startDestination="@id/loginFragment">
        <fragment
            android:id="@+id/loginFragment"
            android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"
            android:label="login_fragment"
            tools:layout="@layout/login_fragment">
            <action
                android:id="@+id/action_loginFragment_to_signUpFragment"
                app:destination="@id/signUpFragment" />

        </fragment>
    </navigation>

这是 LoginFragment 中用于导航的代码 -

binding.signUpLink.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_loginFragment_to_signUpFragment, null));

这是 NavHostFragment 的活动布局文件的摘录 -

<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:name="android.navigation.fragment.NavHostFragment"
    app:navGraph="@navigation/main_navigation"
    app:defaultNavHost="true"/>
4

16 回答 16

264

官方推荐的解决方案

目前使用 FragmentContainerView 不是很友好,你必须从 supportFragmentManager 中访问它:

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

在我的 xml 中,我的 FragmentContainerView 如下所示:

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="parent"
    app:navGraph="@navigation/nav_graph"
    />

这个已经用androidx导航版测试过了2.3.0

我已删除旧答案,因为 Google 开发人员已澄清它不再是推荐的解决方案。感谢@Justlearnedit,发表评论允许我更新此问题。

于 2020-03-09T09:26:25.083 回答
93

更新的解决方案

实际上,Navigation 在 FrameLayout 中找不到 NavController。所以替换<FrameLayout><fragment>将使其工作

<fragment>在标签内添加以下内容-

android:name="androidx.navigation.fragment.NavHostFragment"

进行更改后,代码将与此类似 -

 <fragment
       android:id="@+id/fragment_container"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layout_behavior="@string/appbar_scrolling_view_behavior"
       android:name="androidx.navigation.fragment.NavHostFragment"
       app:navGraph="@navigation/main_navigation"
       app:defaultNavHost="true"/>
于 2018-05-25T21:40:38.917 回答
46

在做了一些研究之后,我在 Google 的 bugtracker 上也发现了这个问题。所以这里是Java的官方解决方案:

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
                .findFragmentById(R.id.nav_host_fragment);
NavController navCo = navHostFragment.getNavController();

在 Kotlin 中:

val navHostFragment = supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
于 2020-02-27T14:02:09.267 回答
37

Activity.onCreate()原因是,如果您使用FragmentContainerView(或仅使用)添加片段视图,则该方法内部不可用FrameLayout。在这种情况下,获取 的正确方法NavController是找到NavHostFragment并从中获取控制器。查看问题解释

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

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

不要 像其他一些答案所建议<fragment>的那样使用。<androidx.fragment.app.FragmentContainerView>请参阅我上面提到的问题中 Google 团队的评论

您应该始终使用 FragmentContainerView。当片段的根布局直接位于其他布局(如 ConstraintLayout)中时,除了通过该标签添加的片段经历与其他片段完全不同的生命周期状态的标签的潜在问题外,绝对还有其他解决方案围绕窗口插入和布局问题发生添加到 FragmentManager。Lint 检查的存在正是因为您绝对应该在所有情况下切换到 FragmentContainerView。

AndroidDevSummit 2019还发布了一个公告,解释了FragmentContainerView引入的原因,以及关于差异的另一个线程:<androidx.fragment.app.FragmentContainerView> vs as a view for a NavHost

于 2020-08-07T08:16:20.940 回答
7

只需替换为<FrameLayout><fragment>替换android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"android:name="androidx.navigation.fragment.NavHostFragment"

于 2021-05-10T15:23:36.267 回答
5

使用onViewCreated等fragment的视图

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val navController = Navigation.findNavController(view)

    binding.signUpLink.setOnClickListener {
            navController.navigate(R.id.action_loginFragment_to_signUpFragment)
    }
于 2018-09-07T13:06:29.090 回答
5

我遇到了同样的问题。所以,而不是这个,

binding.signUpLink.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_loginFragment_to_signUpFragment, null));

我用我NavHostFragment的找到NavHostFragment

Button button = (Button)findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Fragment navhost = getSupportFragmentManager().findFragmentById(R.id.fragment2);
                NavController c = NavHostFragment.findNavController(navhost);
                c.navigate(R.id.firstFragment);


            }
        });

fragment2navhostfragmentID

于 2019-11-12T08:58:53.177 回答
5

在我的情况下,它是由 android studio 完成的......

<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:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

上面的代码是被警告替换的工作代码,如下所示!

<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_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
于 2021-01-21T13:58:07.150 回答
4

在 Java 中尝试以下行:

Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);
于 2020-05-18T04:17:33.480 回答
4

我有以下错误:

MainActivity@9ff856 在 2131230894 上没有设置 NavController

我正在使用底部导航视图:

以下对我有用:

val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)

val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)
val navController = navHostFragment?.findNavController()
if (navController != null) {
    bottomNavigationView.setupWithNavController(navController)
}
于 2021-02-13T16:44:04.377 回答
0

一件奇怪的事情发生在我身上,下面的代码片段正在处理从 Fragment1 到 fragment2 的正常流程,但是在进入 fragment1 并再次导航 fragment2 之后,这引发了“未为视图设置导航控制器”错误。

    binding.ivIcon.setOnClickListener(v -> {
            Openfragment2(v);});

private void Openfragment2(View view) {
    Navigation.findNavController(binding.ivIcon).navigate(R.id.fragment2);

}

这里有问题,在 findNavController 中需要传递 onclicked 视图。

private void Openfragment2(View view) {
    Navigation.findNavController(view).navigate(R.id.fragment2);

}
于 2019-12-06T08:25:10.907 回答
0

我刚才遇到了这个问题,但我确定我的代码然后意识到我已经将片段的位置从主包下更改到另一个文件夹

所以我用 Build-> clean 然后 Build -> make project 解决了这个问题,让 IDE 改变它的 Directions 类

于 2020-03-26T11:07:57.603 回答
0

就我而言,我更改了我的代码

val action =
        StartFragmentDirections.actionStartFragmentToLoginFragment()
    Navigation.findNavController(view).navigate(action);

onViewCreated()

于 2021-02-05T02:48:09.620 回答
0

这段代码应该适用于 kotlin

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_loginFlow_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.to_dictionaryDownload1)
于 2021-08-11T12:21:33.533 回答
0

在我的情况下(我正在使用BottomNavigation),findNavController我正在添加FramlayoutId!您应该fragment像这样添加 ID:

在我的 xml 中:

<FrameLayout
            android:id="@+id/container_orders"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/bnv_main"
            android:visibility="gone">

            <fragment
                android:id="@+id/nav_orders"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="false" />

        </FrameLayout>

在 Kotlin 类中:

private val navOrdersController by lazy {
        mainActivity.findNavController(R.id.nav_orders).apply {
            graph = navInflater.inflate(R.navigation.main_navigation_graph).apply {
                startDestination = startDestinations.getValue(R.id.action_history)
            }
        }
    }
于 2021-11-17T10:10:29.913 回答
0

私有lateinit var绑定:ActivityNewsBinding

lateinit var viewModel: NewsViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //setContentView(R.layout.activity_news)
    binding = ActivityNewsBinding.inflate(layoutInflater)
    setContentView(binding.root)

    val newsrepository = NewsRepository(ArticleDatabase(this))
    val viewModelProviderFactory = NewsViewModelProviderFactory(newsrepository)
    viewModel = ViewModelProvider(this,viewModelProviderFactory).get(NewsViewModel::class.java)

    
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.newsNavHostFragment) as NavHostFragment
    val navController = navHostFragment.navController
    binding.bottomNavigationView.setupWithNavController(navController)


}
于 2022-03-02T13:23:32.487 回答