当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
其他导航都很好,除了这个。
我使用Fragment的findNavController()函数来访问NavController。
任何帮助都将不胜感激。
当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
其他导航都很好,除了这个。
我使用Fragment的findNavController()函数来访问NavController。
任何帮助都将不胜感激。
当前回答
当我按了两次后退键时,我想到了这个问题。首先,我拦截KeyListener并覆盖KeyEvent.KEYCODE_BACK。我在名为OnResume的函数中添加了下面的代码,然后解决了这个问题/问题。
override fun onResume() {
super.onResume()
view?.isFocusableInTouchMode = true
view?.requestFocus()
view?.setOnKeyListener { v, keyCode, event ->
if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) {
activity!!.finish()
true
}
false
}
}
当我第二次遇到这种情况时,它的状态与第一次相同,我发现我可能会使用add函数。让我们来分析一下这些情况。
首先,FragmentA导航到FragmentB,然后FragmentB导航到FragmentA,然后按下返回按钮…崩溃出现了。 其次,FragmentA导航到FragmentB,然后FragmentB导航到FragmentC, FragmentC导航到FragmentA,然后按下返回按钮…崩溃出现了。
所以我认为当按下返回按钮时,FragmentA会回到FragmentB或FragmentC,这会导致登录混乱。最后,我发现名为popBackStack的函数可以用于返回,而不是导航。
NavHostFragment.findNavController(this@TeacherCloudResourcesFragment).
.popBackStack(
R.id.teacher_prepare_lesson_main_fragment,false
)
到目前为止,问题已经真正解决了。
其他回答
您可以在导航控制器的当前目标中检查请求的操作。
更新 为安全导航增加了全局操作的使用。
fun NavController.navigateSafe(
@IdRes resId: Int,
args: Bundle? = null,
navOptions: NavOptions? = null,
navExtras: Navigator.Extras? = null
) {
val action = currentDestination?.getAction(resId) ?: graph.getAction(resId)
if (action != null && currentDestination?.id != action.destinationId) {
navigate(resId, args, navOptions, navExtras)
}
}
在我的例子中,如果用户非常非常快地点击同一个视图两次,这个崩溃就会发生。所以你需要实现某种逻辑来防止多次快速点击……这很烦人,但似乎是必要的。
你可以在这里阅读更多关于防止这种情况的内容:Android防止双击按钮
编辑3/19/2019:为了进一步澄清一点,这个崩溃不是仅仅通过“非常非常快地单击同一个视图两次”就可以完全重现的。或者,您可以使用两个手指同时单击两个(或更多)视图,其中每个视图都有自己的导航。当你有一个项目列表时,这尤其容易做到。以上关于多次点击预防的信息将处理这种情况。
编辑4/16/2020:以防你对上面的Stack Overflow帖子不太感兴趣,我包括了我自己的(Kotlin)解决方案,我已经使用了很长一段时间了。
OnSingleClickListener.kt
class OnSingleClickListener : View.OnClickListener {
private val onClickListener: View.OnClickListener
constructor(listener: View.OnClickListener) {
onClickListener = listener
}
constructor(listener: (View) -> Unit) {
onClickListener = View.OnClickListener { listener.invoke(it) }
}
override fun onClick(v: View) {
val currentTimeMillis = System.currentTimeMillis()
if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
previousClickTimeMillis = currentTimeMillis
onClickListener.onClick(v)
}
}
companion object {
// Tweak this value as you see fit. In my personal testing this
// seems to be good, but you may want to try on some different
// devices and make sure you can't produce any crashes.
private const val DELAY_MILLIS = 200L
private var previousClickTimeMillis = 0L
}
}
ViewExt.kt
fun View.setOnSingleClickListener(l: View.OnClickListener) {
setOnClickListener(OnSingleClickListener(l))
}
fun View.setOnSingleClickListener(l: (View) -> Unit) {
setOnClickListener(OnSingleClickListener(l))
}
HomeFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsButton.setOnSingleClickListener {
// navigation call here
}
}
我在我的项目中也有同样的问题,首先我试图在视图上触发导航动作的点击,但经过一些实验后,我发现在真正缓慢的设备上,debounce应该是一个非常高的值,导致应用程序对快速设备的用户感到缓慢。
所以我为NavController提出了以下扩展,我认为它符合原始的API,并且易于使用:
fun NavController.safeNavigate(directions: NavDirections) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, null)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
fun NavController.safeNavigate(directions: NavDirections, navOptions: NavOptions?) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, navOptions)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
fun NavController.safeNavigate(directions: NavDirections, navigatorExtras: Navigator.Extras) {
try {
currentDestination?.getAction(directions.actionId) ?: return
navigate(directions.actionId, directions.arguments, null, navigatorExtras)
} catch (e : Exception) {
logError("Navigation error", e)
}
}
请注意,我正在使用SafeArgs和NavDirections。这些函数检查操作从当前目的地是否有效,并且仅在操作不为空时进行导航。如果Navigation库每次都返回正确的操作,那么try catch部分就不需要了,但我希望消除所有可能的崩溃。
在我的例子中,我使用了一个自定义的后退按钮来向上导航。我调用了onBackPressed()而不是下面的代码
findNavController(R.id.navigation_host_fragment).navigateUp()
这导致发生IllegalArgumentException。在我将其更改为使用navigateUp()方法之后,我就不会再次崩溃了。
一个荒谬但非常强大的方法是: 简单地称之为:
view?.findNavController()?.navigateSafe(action)
只需创建这个扩展:
fun NavController.navigateSafe(
navDirections: NavDirections? = null
) {
try {
navDirections?.let {
this.navigate(navDirections)
}
}
catch (e:Exception)
{
e.printStackTrace()
}
}