IllegalStateException: Link 没有设置 NavController
IllegalStateException: Link does not have a NavController set
我正在使用 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 的 activity 布局文件的摘录 -
<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"/>
更新的解决方案
实际上,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"/>
使用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)
}
我遇到了同样的问题。
所以,而不是这个,
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);
}
});
fragment2
是 navhostfragmentID
。
我发生了一件奇怪的事情,下面的代码片段在从 Fragment1 到 fragment2 的正常流程上工作,但是在到达 fragment1 并再次导航 fragment2 之后,这抛出了 "Navigation controller not set for the view" 错误。
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);
}
经过一番研究,我发现 this Issue also on Google's 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
官方推荐方案
目前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,发表评论让我更新这个问题。
我刚才遇到了这个问题,但我对我的代码很确定,然后意识到我已经将片段的位置从主包下更改到另一个文件夹
所以
我用 Build-> clean 然后 Build ->make project 解决了这个问题,让 IDE 改变它的方向 class
在 Java 中尝试以下行:
Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);
原因是片段视图在 Activity.onCreate()
方法中不可用,如果您使用 FragmentContainerView
(或只是 FrameLayout
)添加它。在这种情况下获取 NavController
的正确方法是找到 NavHostFragment
并从中获取控制器。见 issue and the explanation.
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 团队的 comment。
You should always use FragmentContainerView. There are absolutely other fixes around window insets and layout issues that occur when a fragment's root layout is directly within other layouts such as ConstraintLayout, besides the underlying issues with the tag where fragments added via that tag go through lifecycle states entirely differently from the other Fragments added to the FragmentManager. The Lint check is there exactly because you absolutely should switch over to FragmentContainerView in all cases.
还有一个announcement from AndroidDevSummit 2019 which explains why FragmentContainerView
was introduced, and another thread on SO about the difference:
在我的案例中,它是由 android 工作室完成的...
<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" />
在我的例子中,我更改了我的代码
val action =
StartFragmentDirections.actionStartFragmentToLoginFragment()
Navigation.findNavController(view).navigate(action);
在onViewCreated()
我遇到了以下错误:
MainActivity@9ff856 does not have a NavController set on 2131230894
我使用的是底部导航视图:
以下对我有用:
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)
val navController = navHostFragment?.findNavController()
if (navController != null) {
bottomNavigationView.setupWithNavController(navController)
}
只需将<FrameLayout>
替换为<fragment>
并将android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"
替换为android:name="androidx.navigation.fragment.NavHostFragment"
此代码应该适用于 kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_loginFlow_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.to_dictionaryDownload1)
在我的例子中(我使用的是 BottomNavigation
),在 findNavController
中我添加了 Framlayout
ID!您应该像这样添加 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 中 class:
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)
}
}
}
私有 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)
}
FragmentViewContainer 的更新答案
确保你有
android:name="androidx.navigation.fragment.NavHostFragment"
而不是您想要的片段作为起始片段。
(起始目的地在导航图中定义)。
我正在使用 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 的 activity 布局文件的摘录 -
<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"/>
更新的解决方案
实际上,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"/>
使用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)
}
我遇到了同样的问题。 所以,而不是这个,
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);
}
});
fragment2
是 navhostfragmentID
。
我发生了一件奇怪的事情,下面的代码片段在从 Fragment1 到 fragment2 的正常流程上工作,但是在到达 fragment1 并再次导航 fragment2 之后,这抛出了 "Navigation controller not set for the view" 错误。
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);
}
经过一番研究,我发现 this Issue also on Google's 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
官方推荐方案
目前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,发表评论让我更新这个问题。
我刚才遇到了这个问题,但我对我的代码很确定,然后意识到我已经将片段的位置从主包下更改到另一个文件夹
所以 我用 Build-> clean 然后 Build ->make project 解决了这个问题,让 IDE 改变它的方向 class
在 Java 中尝试以下行:
Navigation.findNavController(findViewById(R.id.nav_host_fragment)).navigate(R.id.first_fragment);
原因是片段视图在 Activity.onCreate()
方法中不可用,如果您使用 FragmentContainerView
(或只是 FrameLayout
)添加它。在这种情况下获取 NavController
的正确方法是找到 NavHostFragment
并从中获取控制器。见 issue and the explanation.
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 团队的 comment。
You should always use FragmentContainerView. There are absolutely other fixes around window insets and layout issues that occur when a fragment's root layout is directly within other layouts such as ConstraintLayout, besides the underlying issues with the tag where fragments added via that tag go through lifecycle states entirely differently from the other Fragments added to the FragmentManager. The Lint check is there exactly because you absolutely should switch over to FragmentContainerView in all cases.
还有一个announcement from AndroidDevSummit 2019 which explains why FragmentContainerView
was introduced, and another thread on SO about the difference:
在我的案例中,它是由 android 工作室完成的...
<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" />
在我的例子中,我更改了我的代码
val action =
StartFragmentDirections.actionStartFragmentToLoginFragment()
Navigation.findNavController(view).navigate(action);
在onViewCreated()
我遇到了以下错误:
MainActivity@9ff856 does not have a NavController set on 2131230894
我使用的是底部导航视图:
以下对我有用:
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)
val navController = navHostFragment?.findNavController()
if (navController != null) {
bottomNavigationView.setupWithNavController(navController)
}
只需将<FrameLayout>
替换为<fragment>
并将android:name="org.fossasia.openevent.app.core.auth.login.LoginFragment"
替换为android:name="androidx.navigation.fragment.NavHostFragment"
此代码应该适用于 kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_loginFlow_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.to_dictionaryDownload1)
在我的例子中(我使用的是 BottomNavigation
),在 findNavController
中我添加了 Framlayout
ID!您应该像这样添加 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 中 class:
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)
}
}
}
私有 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)
}
FragmentViewContainer 的更新答案
确保你有
android:name="androidx.navigation.fragment.NavHostFragment"
而不是您想要的片段作为起始片段。 (起始目的地在导航图中定义)。