skJosh
skJosh

Reputation: 661

NavHostFragment not accessible from fragment in android

I tried to access NavController from the home fragment but had no success. Spent the whole day but still had no success at all. The app crashes in HomeFragment in this line of code:

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

Here is the code that I have implemented:

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
    }
}

This is xml implementation of 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()
    }
}

Error Logs

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

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'
}

Have spent lots of time trying to find a solution on google but nothing works and now I am stuck. It crashes when trying to access requireActivity().findNavController(R.id.nav_host_fragment) in HomeFragment but no crashes with the same code in MainActivity as seen on code snippet.

Any help is highly appreciable.

Upvotes: 1

Views: 2774

Answers (2)

Zain
Zain

Reputation: 40888

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

You can't access the NavController because you try to access it before the setContentView() of the activity get finished; i.e you are trying to access a resource R.id.nav_host_fragment that doesn't exist yet.

And the reason because the HomeFragment is initially hosted by the activity, and it's a part of the activity layout; and in order to draw the fragment on the activity, it needs to take a chance first to get the fragment layout before returning the entire layout with setContentView()

To verify that add the below log messages in the 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")

    }

Fragment:

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
    }

The outcome:

 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

How to solve this:

You can set the navController in lifecycle methods beyond onCreateView() & onViewCreated() of the HomeFragment like in onStart(), or onResume()

class HomeFragment : Fragment() {

    // .... Code omitted

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

Upvotes: 2

Sanket Shah
Sanket Shah

Reputation: 36

Seems like you haven't included content_main.xml in your activity_main.xml

Upvotes: 0

Related Questions