Android底部导航栏实现报错:FragmentContainerView填充失败
问题描述
我是Android开发新手,尝试为应用实现可切换4个Fragment(首页、搜索、收藏、设置)的底部导航栏,但遇到XML相关报错,提示无法初始化FragmentContainerView。
报错核心信息
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.masala_food_recipes/com.example.masala_food_recipes.MainActivity}: android.view.InflateException: Binary XML file line #28 in com.example.masala_food_recipes:layout/activity_main: Binary XML file line #28 in com.example.masala_food_recipes:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
相关代码
activity_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" tools:context=".MainActivity"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container_view" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" style="@style/bottomNavigationBackground" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingHorizontal="8dp" app:itemActiveIndicatorStyle="@style/Indicator" app:itemIconTint="@color/icon_color" app:itemPaddingBottom="16dp" app:itemPaddingTop="12dp" app:itemRippleColor="@color/light_orange" app:itemTextColor="@color/label_color" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/bottom_navigation_menu" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity_main.kt
package com.example.masala_food_recipes import android.annotation.SuppressLint import android.content.Context import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.navigation.NavController import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.setupWithNavController import com.example.masala_food_recipes.data.DataManager import com.example.masala_food_recipes.data.interactors.Cuisines import com.example.masala_food_recipes.data.interactors.ForYouRecipe import com.example.masala_food_recipes.data.interactors.SearchRecipe import com.example.masala_food_recipes.data.interactors.UnderFiveIngredient import com.example.masala_food_recipes.data.interactors.UnderTwentyMinRecipe import com.example.masala_food_recipes.databinding.ActivityMainBinding import com.example.masala_food_recipes.ui.fragment.FavouriteFragment import com.example.masala_food_recipes.ui.fragment.HomeFragment import com.example.masala_food_recipes.ui.fragment.SearchFragment import com.example.masala_food_recipes.ui.fragment.SettingFragment class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var navController: NavController private lateinit var homeScreen: HomeFragment private val favouriteScreen = FavouriteFragment() private lateinit var searchScreen : SearchFragment private val settingScreen = SettingFragment() private val allRecipes by lazy { DataManager(this).getAllRecipesData() } private val under20MinList by lazy { UnderTwentyMinRecipe(allRecipes).execute() } private val under5IngredientList by lazy { UnderFiveIngredient(allRecipes).execute() } private val cuisineList by lazy { Cuisines(allRecipes).getCuisineCards() } private val forYouList by lazy { ForYouRecipe(allRecipes).execute() } private val searchList by lazy { SearchRecipe(allRecipes).execute() } @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { val sharedPref = this.getSharedPreferences("MY_PREFS", Context.MODE_PRIVATE) if (sharedPref.getBoolean("current_state", false)) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) } super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) init() } private fun init() { homeScreen = HomeFragment(cuisineList, forYouList, under20MinList, under5IngredientList) searchScreen = SearchFragment(searchList) val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_container_view) as NavHostFragment navController = navHostFragment.navController binding.bottomNavigation.setupWithNavController(navController) } @Deprecated("Deprecated in Java") override fun onBackPressed() { val navController = findNavController(R.id.fragment_container_view) val currentFragmentId = navController.currentDestination?.id if (currentFragmentId == R.id.homeFragment) { val builder = AlertDialog.Builder(this) builder.setMessage("Are you sure you want to exit?").setCancelable(false) .setPositiveButton("Yes") { _, _ -> finish() } .setNegativeButton("No") { dialog, _ -> dialog.cancel() } val alert = builder.create() alert.show() } else navController.popBackStack() } }
完整报错日志
Process: com.example.masala_food_recipes, PID: 18474 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.masala_food_recipes/com.example.masala_food_recipes.MainActivity}: android.view.InflateException: Binary XML file line #28 in com.example.masala_food_recipes:layout/activity_main: Binary XML file line #28 in com.example.masala_food_recipes:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
解决方案
1. 移除冗余布局组件
布局中同时存在ViewPager2和NavHostFragment,两者都是Fragment容器,选其一即可。既然用Navigation组件实现底部导航,直接删除ViewPager2代码,修改后的activity_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" tools:context=".MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container_view" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/navigation" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" style="@style/bottomNavigationBackground" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingHorizontal="8dp" app:itemActiveIndicatorStyle="@style/Indicator" app:itemIconTint="@color/icon_color" app:itemPaddingBottom="16dp" app:itemPaddingTop="12dp" app:itemRippleColor="@color/light_orange" app:itemTextColor="@color/label_color" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/bottom_navigation_menu" /> </androidx.constraintlayout.widget.ConstraintLayout>
2. 删除手动实例化Fragment的代码
Navigation组件会自动根据导航图管理Fragment实例,手动创建的homeScreen、searchScreen等实例完全多余,还可能导致冲突。修改activity_main.kt,移除这些代码:
package com.example.masala_food_recipes import android.annotation.SuppressLint import android.content.Context import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.navigation.NavController import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import com.example.masala_food_recipes.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var navController: NavController @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { val sharedPref = this.getSharedPreferences("MY_PREFS", Context.MODE_PRIVATE) if (sharedPref.getBoolean("current_state", false)) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) } super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) init() } private fun init() { val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_container_view) as NavHostFragment navController = navHostFragment.navController binding.bottomNavigation.setupWithNavController(navController) } @Deprecated("Deprecated in Java") override fun onBackPressed() { val currentFragmentId = navController.currentDestination?.id if (currentFragmentId == R.id.homeFragment) { val builder = AlertDialog.Builder(this) builder.setMessage("确定要退出吗?").setCancelable(false) .setPositiveButton("是") { _, _ -> finish() } .setNegativeButton("否") { dialog, _ -> dialog.cancel() } val alert = builder.create() alert.show() } else { navController.popBackStack() } } }
3. 配置导航图与菜单对应关系
确保res/navigation/navigation.xml存在,并且里面的Fragment目的地ID和底部导航菜单res/menu/bottom_navigation_menu.xml的Item ID完全一致:
导航示例(navigation.xml)
<navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation" app:startDestination="@id/homeFragment"> <fragment android:id="@+id/homeFragment" android:name="com.example.masala_food_recipes.ui.fragment.HomeFragment" android:label="首页" /> <fragment android:id="@+id/searchFragment" android:name="com.example.masala_food_recipes.ui.fragment.SearchFragment" android:label="搜索" /> <fragment android:id="@+id/favouriteFragment" android:name="com.example.masala_food_recipes.ui.fragment.FavouriteFragment" android:label="收藏" /> <fragment android:id="@+id/settingFragment" android:name="com.example.masala_food_recipes.ui.fragment.SettingFragment" android:label="设置" /> </navigation>
底部菜单示例(bottom_navigation_menu.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/homeFragment" android:icon="@drawable/ic_home" android:title="首页" /> <item android:id="@+id/searchFragment" android:icon="@drawable/ic_search" android:title="搜索" /> <item android:id="@+id/favouriteFragment" android:icon="@drawable/ic_favourite" android:title="收藏" /> <item android:id="@+id/settingFragment" android:icon="@drawable/ic_setting" android:title="设置" /> </menu>
4. 用ViewModel传递数据
原来手动给Fragment传参的方式不符合Navigation规范,改用ViewModel统一管理数据:
创建RecipeViewModel
import androidx.lifecycle.ViewModel import com.example.masala_food_recipes.data.DataManager import com.example.masala_food_recipes.data.interactors.* class RecipeViewModel(private val context: android.content.Context) : ViewModel() { val allRecipes by lazy { DataManager(context).getAllRecipesData() } val under20MinList by lazy { UnderTwentyMinRecipe(allRecipes).execute() } val under5IngredientList by lazy { UnderFiveIngredient(allRecipes).execute() } val cuisineList by lazy { Cuisines(allRecipes).getCuisineCards() } val forYouList by lazy { ForYouRecipe(allRecipes).execute() } val searchList by lazy { SearchRecipe(allRecipes).execute() } }
在Fragment中获取ViewModel
以HomeFragment为例:
import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModels class HomeFragment : Fragment() { private val viewModel: RecipeViewModel by viewModels { object : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return RecipeViewModel(requireContext()) as T } } } override fun onViewCreated(view: android.view.View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 使用viewModel.cuisineList、viewModel.forYouList等数据 } }
完成以上修改后,重新构建项目即可解决报错,底部导航栏就能正常切换四个Fragment了。
内容的提问来源于stack exchange,提问作者Zeyad Galal
相关产品推荐
相关产品推荐

