无法从 android 中的片段访问 NavHostFragment

NavHostFragment not accessible from fragment in android

我尝试从主页片段访问 NavController 但没有成功。花了一整天,但仍然没有成功。该应用程序在 HomeFragment 这行代码中崩溃:

navController = requireActivity().findNavController(R.id.nav_host_fragment)

这是我实现的代码:

HomeFragmnet

class HomeFragment : Fragment() {

    private lateinit var homeViewModel: HomeViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var navController: NavController

    private var itemList: MutableList<Home?> = ArrayList()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        navController = requireActivity().findNavController(R.id.nav_host_fragment)
        homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
        val root = inflater.inflate(R.layout.fragment_home, container, false)
        recyclerView = root.findViewById(R.id.homeRecyclerView)
        return root
    }
}

这是 xml nav_bar.xml

的实现
<?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/nav_home">

    <fragment
        android:id="@+id/nav_home"
        android:name="fi.gambitlabs.pworker.ui.home.HomeFragment"
        android:label="@string/menu_home"
        tools:layout="@layout/fragment_home" />

</navigation>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <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/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity

class MainActivity : AppCompatActivity() {

    private lateinit var appBarConfiguration: AppBarConfiguration
    private lateinit var navController: NavController
    private lateinit var navView: NavigationView

    override fun onCreate(savedInstanceState: Bundle?) {
        // Disable dark mode
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)
        
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        navView = findViewById(R.id.nav_view)
        navController = findNavController(R.id.nav_host_fragment)
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        appBarConfiguration = AppBarConfiguration(setOf(
                R.id.nav_home), drawerLayout)
        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.main, menu)
        return true
    }

    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
}

错误日志

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: fi.gambitlabs.pworker, PID: 12300
    java.lang.RuntimeException: Unable to start activity ComponentInfo{fi.gambitlabs.pworker/fi.gambitlabs.pworker.MainActivity}: android.view.InflateException: Binary XML file line #14 in fi.gambitlabs.pworker:layout/activity_main: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: android.view.InflateException: Binary XML file line #14 in fi.gambitlabs.pworker:layout/activity_main: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
     Caused by: android.view.InflateException: Binary XML file line #19 in fi.gambitlabs.pworker:layout/content_main: Error inflating class fragment
     Caused by: java.lang.IllegalArgumentException: ID does not reference a View inside this Activity
        at android.app.Activity.requireViewById(Activity.java:3375)
        at androidx.core.app.ActivityCompat.requireViewById(ActivityCompat.java:366)
        at androidx.navigation.Navigation.findNavController(Navigation.java:58)
        at *emphasized text*androidx.navigation.ActivityKt.findNavController(Activity.kt:30)
        at fi.gambitlabs.pworker.ui.home.HomeFragment.onCreateView(HomeFragment.kt:30)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
        at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3065)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2988)
        at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:392)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:281)
        at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:140)
        at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
        at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:319)
        at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:298)
        at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1067)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:995)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
        at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1261)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1117)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
E/AndroidRuntime:     at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1261)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1117)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:479)
        at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:699)
        at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:195)
        at fi.gambitlabs.Pworker.MainActivity.onCreate(MainActivity.kt:28)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

app_bar_main.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=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.PWorker.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/Theme.PWorker.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

依赖项

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.navigation:navigation-fragment:2.3.5'
    implementation 'androidx.navigation:navigation-ui:2.3.5'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    //Firebase
    implementation platform('com.google.firebase:firebase-bom:28.2.0')
    implementation 'com.google.firebase:firebase-analytics-ktx'
    implementation 'com.google.firebase:firebase-crashlytics-ktx'
}

我花了很多时间试图在 google 上找到解决方案,但没有任何效果,现在我被困住了。尝试访问 HomeFragment 中的 requireActivity().findNavController(R.id.nav_host_fragment) 时崩溃,但在 MainActivity 中使用与代码片段相同的代码没有崩溃。

非常感谢任何帮助。

您的 activity_main.xml

中似乎没有包含 content_main.xml

I tried to access NavController from the home fragment but had no success.

您无法访问 NavController,因为您试图在 activity 的 setContentView() 完成之前访问它;即您正在尝试访问尚不存在的资源 R.id.nav_host_fragment

原因是 HomeFragment 最初由 activity 托管,并且它是 activity 布局的一部分;为了在 activity 上绘制片段,需要先尝试获取片段布局,然后再使用 setContentView()

返回整个布局

验证是否在 activity (MainActivity) & HomeFragment:

中添加了以下日志消息

Activity:

class MainActivity : AppCompatActivity() {

    // .... Code omitted

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

        Log.d(TAG, "MainActivity onCreate: BEFORE setContentView()")

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        Log.d(TAG, "MainActivity onCreate: AFTER setContentView()")

        // .... Code omitted

        Log.d(TAG, "MainActivity onCreate: END")

    }

片段:

class HomeFragment : Fragment() {

    // .... Code omitted

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        Log.d(TAG, "HomeFragment onCreateView: START")

        homeViewModel =
            ViewModelProvider(this).get(HomeViewModel::class.java)

        // .... Code omitted

        Log.d(TAG, "HomeFragment onCreateView: END")

        return root
    }

结果:

 D/LOG_TAG: MainActivity onCreate: BEFORE setContentView()
 D/LOG_TAG: HomeFragment onCreateView: START
 D/LOG_TAG: HomeFragment onCreateView: END
 D/LOG_TAG: MainActivity onCreate: AFTER setContentView()
 D/LOG_TAG: MainActivity onCreate: END

如何解决:

您可以在 onStart()onResume() 中的 HomeFragmentonCreateView()onViewCreated() 以外的生命周期方法中设置 navController

class HomeFragment : Fragment() {

    // .... Code omitted

    override fun onStart() {
        super.onStart()
        val navController = requireActivity().findNavController(R.id.nav_host_fragment)
    }