Null NavHostFragment/NavController with FragmentContainerView

匆匆过客 提交于 2020-06-16 20:02:01

问题


Expected

Create bottom app bar navigation using Navigation UI's BottomNavigationView and FragmentContainerView similar to the sample app NavigationAdavancedSample.

Note, NavigationAndvancedSample app does not appear to use the same implementation as described in the documentation as it has a workaround implementation using the custom extension method setupWithNavController.

Observed

Null NavHostFragment when creating the BottomNavigationView in the MainActivity.kt with a FragmentContainerView.

The reproducible error and full code sample is available in the BottomNavigationViewSample repository.

Error

2020-05-15 12:39:19.117 18747-18747/com.example.bottomnavigationviewsample E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.bottomnavigationviewsample, PID: 18747 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.bottomnavigationviewsample/com.example.bottomnavigationviewsample.MainActivity}: kotlin.TypeCastException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 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:2016) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) Caused by: kotlin.TypeCastException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment at com.example.bottomnavigationviewsample.MainActivity.onCreate(MainActivity.kt:16) at android.app.Activity.performCreate(Activity.java:7802) at android.app.Activity.performCreate(Activity.java:7791) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)  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:2016)  at android.os.Handler.dispatchMessage(Handler.java:107)  at android.os.Looper.loop(Looper.java:214)  at android.app.ActivityThread.main(ActivityThread.java:7356)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)  2020-05-15 12:39:19.132 18747-18747/com.example.bottomnavigationviewsample I/Process: Sending signal. PID: 18747 SIG: 9

Implementation

  1. Create a navigation graph for the first view, home.xml, to show in the BottomNavigationView under app > res > navigation.

    a. When prompted to Add Project Dependency, select OK.

    b. Add kotlinOptions { jvmTarget = '1.8' } to build.gradle (:someAppModule) to enable inline bytecode for the AppBarConfiguration.

    c. Add a fragment to home.xml to display in the BottomNavigationView.

home.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/home"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.bottomnavigationviewsample.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
</navigation>

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    kotlinOptions { jvmTarget = '1.8' }

    defaultConfig {
        applicationId "com.example.bottomnavigationviewsample"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
}

  1. Create a menu for the BottomNavigationView, bottom_nav.xml, under app > res > menu.

    a. Add string values for the menu id and title.

    b. Add vector drawable for the menu icon.

bottom_nav.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/home"
            android:icon="@drawable/ic_home_black_24dp"
            android:contentDescription="@string/cd_home"
            android:title="@string/title_home" />
</menu>
  1. Add the FragmentContainerView and BottomNavigationView to MainActivity.kt's activity_main.xml layout.

activity_main.xml

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_nav"/>
</LinearLayout>
  1. Enable the BottomNavigationView in MainActivity.kt.

    a. Create AppBarConfiguration. See: Update UI components with NaviationUI > AppBarConfiguration

    b. Create the NavHostFragment and NavController. See: StackOverflow solution for FragmentContainerView using findNavController

    c. Set the action bar navigation.

    d. Set the BottomNavigationView navigation. See: Update UI components with NavigationUI > Bottom navigation

MainActivity.kt

package com.example.bottomnavigationviewsample

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val appBarConfiguration = AppBarConfiguration(setOf(R.id.home))

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_container) as NavHostFragment
        val navController = navHostFragment.navController

        setupActionBarWithNavController(navController, appBarConfiguration)
        bottom_nav.setupWithNavController(navController)
    }
}

Attempted Solutions

  1. Create the NavController with findNavController(R.id.nav_host_container) as outlined in the documentation.

  2. Implement a fragment view instead of FragmentContainerView in activity_main.xml.

activity_main.xml

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_nav"/>
</LinearLayout>

回答1:


Add a parent navigation graph for the BottomNavigationView menu items.

The full sample code is included within BottomNavigationSample.

  1. Create a parent level navigation graph for the menu fragments. Note, this is not reflected in the NavigationAdvancedSample architecture pattern which has three separate navigation graphs without a parent navigation graph.

This can be accomplished with one parent navigation graph and fragments or nested navigation graphs for each menu item. Nested navigation graphs are good because each sub-flow can be organized within the nested graph.

main.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"
    android:id="@+id/main"
    app:startDestination="@id/home">
    <include app:graph="@navigation/home" />
    <include app:graph="@navigation/saved" />
</navigation>
  1. Add the parent graph using navGraph in the FragmentContainerView.

activity_main.xml

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_container"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/main"/>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_nav" />

</LinearLayout>
  1. Enable the BottomNavigationView in MainActivity.kt.
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val appBarConfiguration = AppBarConfiguration(setOf(R.id.home, R.id.saved))

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_container) as NavHostFragment
        val navController = navHostFragment.navController

        setupActionBarWithNavController(navController, appBarConfiguration)
        bottom_nav.setupWithNavController(navController)
    }
}


来源:https://stackoverflow.com/questions/61827683/null-navhostfragment-navcontroller-with-fragmentcontainerview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!