You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Navigation导航异常:第二次点击按钮崩溃排查

导航崩溃问题:第二次点击按钮触发IllegalArgumentException

点击id为stavzac的ImageButton,第一次能正常从StaveniFragment导航到StaveniZacatecnikFragment,但第二次点击按钮时应用直接崩溃,Logcat关键报错:

java.lang.IllegalArgumentException: Navigation action/destination com.example.ludva2alpha:id/action_staveniFragment_to_staveniZacatecnikFragment cannot be found from the current destination Destination(com.example.ludva2alpha:id/staveniZacatecnikFragment) label=fragment_staveni_zacatecnik class=com.example.ludva2alpha.StaveniZacatecnikFragment

相关代码

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
    app:startDestination="@id/staveniFragment">

    <fragment
        android:id="@+id/staveniFragment"
        android:name="com.example.ludva2alpha.StaveniFragment"
        android:label="fragment_staveni"
        tools:layout="@layout/fragment_staveni" >
        <action
            android:id="@+id/action_staveniFragment_to_staveniZacatecnikFragment"
            app:destination="@id/staveniZacatecnikFragment" />
    </fragment>
    <fragment
        android:id="@+id/staveniZacatecnikFragment"
        android:name="com.example.ludva2alpha.StaveniZacatecnikFragment"
        android:label="fragment_staveni_zacatecnik"
        tools:layout="@layout/fragment_staveni_zacatecnik" />
</navigation>

StaveniFragment.kt

package com.example.ludva2alpha

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.ludva2alpha.databinding.FragmentStaveniBinding

class StaveniFragment : Fragment() {

    private lateinit var binding: FragmentStaveniBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStaveniBinding.inflate(inflater, container, false)
        val view = binding.root

        val stavzacButton: ImageButton = binding.stavzac

        stavzacButton.setOnClickListener {
            val navController = findNavController()
            navController.navigate(R.id.action_staveniFragment_to_staveniZacatecnikFragment)
        }

        return view
    }
}

MainActivity.kt

package com.example.ludva2alpha

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.bottomnavigation.BottomNavigationView

class MainActivity : AppCompatActivity() {

    private lateinit var bottomNavigationView: BottomNavigationView
    var currentPage = 0  
    private fun updateBottomNavigationBarIcons() {
        when (currentPage) {
            0 -> bottomNavigationView.selectedItemId = R.id.bottom_knihovna
            1 -> bottomNavigationView.selectedItemId = R.id.bottom_staveni
            2 -> bottomNavigationView.selectedItemId = R.id.bottom_domov
            3 -> bottomNavigationView.selectedItemId = R.id.bottom_programovani
            4 -> bottomNavigationView.selectedItemId = R.id.bottom_nastaveni
        }
    }

    private lateinit var navController: NavController
    override fun onSupportNavigateUp(): Boolean {
        navController = findNavController(R.id.navHostFragmentContainerView)
        return navController.navigateUp() || super.onSupportNavigateUp()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Thread.sleep(3000)
        installSplashScreen()
        setContentView(R.layout.activity_main)

        val viewPager: ViewPager2 = findViewById(R.id.view_pager)
        val fragmets: ArrayList<Fragment> = arrayListOf(
            KnihovnaFragment(),
            StaveniFragment(),
            DomovFragment(),
            ProgramovaniFragment(),
            NastaveniFragment()
        )

        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                currentPage = position
                updateBottomNavigationBarIcons()
            }
        })
        val adapter = ViewPagerAdapter(fragmets, this)
        viewPager.adapter = adapter

        bottomNavigationView = findViewById(R.id.bottom_navigation)

        bottomNavigationView.setOnItemSelectedListener { menuItem ->
            val currentItem = when (menuItem.itemId) {
                R.id.bottom_knihovna -> 0
                R.id.bottom_staveni -> 1
                R.id.bottom_domov -> 2
                R.id.bottom_programovani -> 3
                R.id.bottom_nastaveni -> 4
                else -> -1
            }

            if (currentItem >= 0) {
                viewPager.currentItem = currentItem
                true
            } else {
                false
            }
        }

        bottomNavigationView.selectedItemId = R.id.bottom_domov
    }
}

StaveniZacatecnikFragment.kt

package com.example.ludva2alpha

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment

class StaveniZacatecnikFragment : Fragment() {

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

        // 注释掉的导航代码
       /* val imageButton = view.findViewById<ImageButton>(R.id.stavzac)
        imageButton.setOnClickListener {
            findNavController().navigate(StaveniFragmentDirections.actionStaveniFragmentToStaveniZacatecnikFragment())
        }*/
    }
}

问题原因

你同时混用了ViewPager2和Navigation Component两种Fragment管理方式,导致导航栈和ViewPager的Fragment实例不一致:

  1. 第一次点击按钮,从StaveniFragment导航到StaveniZacatecnikFragment,此时Navigation的栈顶是StaveniZacatecnikFragment
  2. 当你通过底部导航切回Staveni页面时,ViewPager2显示的是之前创建的StaveniFragment旧实例,而Navigation的当前目的地还是StaveniZacatecnikFragment
  3. 再次点击按钮时,旧的StaveniFragment实例尝试执行action_staveniFragment_to_staveniZacatecnikFragment动作,但当前导航目的地是StaveniZacatecnikFragment,这个动作不存在于该Fragment的导航配置中,所以抛出异常

解决方案

方案一:统一使用Navigation Component管理底部导航(推荐)

完全移除ViewPager2,改用NavigationUI绑定底部导航和NavController,让Navigation统一管理所有Fragment的生命周期和导航栈:

  1. 修改nav_graph.xml,添加所有底部导航对应的Fragment,并设置正确的startDestination:
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
    app:startDestination="@id/domovFragment">

    <fragment
        android:id="@+id/knihovnaFragment"
        android:name="com.example.ludva2alpha.KnihovnaFragment"
        android:label="fragment_knihovna"
        tools:layout="@layout/fragment_knihovna" />

    <fragment
        android:id="@+id/staveniFragment"
        android:name="com.example.ludva2alpha.StaveniFragment"
        android:label="fragment_staveni"
        tools:layout="@layout/fragment_staveni" >
        <action
            android:id="@+id/action_staveniFragment_to_staveniZacatecnikFragment"
            app:destination="@id/staveniZacatecnikFragment" />
    </fragment>

    <fragment
        android:id="@+id/domovFragment"
        android:name="com.example.ludva2alpha.DomovFragment"
        android:label="fragment_domov"
        tools:layout="@layout/fragment_domov" />

    <fragment
        android:id="@+id/programovaniFragment"
        android:name="com.example.ludva2alpha.ProgramovaniFragment"
        android:label="fragment_programovani"
        tools:layout="@layout/fragment_programovani" />

    <fragment
        android:id="@+id/nastaveniFragment"
        android:name="com.example.ludva2alpha.NastaveniFragment"
        android:label="fragment_nastaveni"
        tools:layout="@layout/fragment_nastaveni" />

    <fragment
        android:id="@+id/staveniZacatecnikFragment"
        android:name="com.example.ludva2alpha.StaveniZacatecnikFragment"
        android:label="fragment_staveni_zacatecnik"
        tools:layout="@layout/fragment_staveni_zacatecnik" />
</navigation>
  1. 修改MainActivity.kt,移除ViewPager2相关代码,改用NavigationUI绑定底部导航:
package com.example.ludva2alpha

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView

class MainActivity : AppCompatActivity() {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Thread.sleep(3000)
        installSplashScreen()
        setContentView(R.layout.activity_main)

        navController = findNavController(R.id.navHostFragmentContainerView)
        val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation)
        
        // 绑定底部导航和NavController
        bottomNavigationView.setupWithNavController(navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}

方案二:保留ViewPager2,修复导航逻辑

如果一定要保留ViewPager2,需要确保点击按钮时,当前导航目的地确实是StaveniFragment:

修改StaveniFragment.kt的点击事件逻辑,添加目的地检查:

stavzacButton.setOnClickListener {
    val navController = findNavController()
    // 检查当前导航目的地是否是StaveniFragment
    if (navController.currentDestination?.id == R.id.staveniFragment) {
        navController.navigate(R.id.action_staveniFragment_to_staveniZacatecnikFragment)
    }
}

另外,当从StaveniZacatecnikFragment返回时,确保ViewPager2的页面切换正确,或者在切换回Staveni页面时重置Fragment实例(可以通过ViewPagerAdapter的createFragment方法每次创建新实例,但这会影响性能)。

内容的提问来源于stack exchange,提问作者Omega

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:05:55