com.google.android.material.bottomnavigation.BottomNavigationView 新手详解(Java 版)
com.google.android.material.bottomnavigation.BottomNavigationView 新手详解(Java版)
文档说明
- 适用人群:安卓新手,首次接触
BottomNavigationView,已了解FragmentContainerView+Navigation基础,贴合你提供的「底部导航+导航容器」经典代码场景 - 核心目标:搞懂该控件的定位/核心优势、逐行拆解你贴的代码属性、掌握菜单资源创建、与Navigation的一键联动(核心用法)、自定义样式和新手避坑,全程基于Java+XML,无Kotlin/Compose
- 贴合你的场景:以你提供的「FragmentContainerView + BottomNavigationView」代码为核心,讲解二者的联动逻辑(这是安卓官方推荐的单Activity多Fragment底部导航架构,企业开发主流写法)
一、先搞懂:BottomNavigationView 是什么?(通俗定位)
BottomNavigationView是Google Material Design(材料设计)风格的官方底部导航栏控件,属于com.google.android.material包,是专门为单Activity多Fragment架构设计的底部导航组件,核心作用是:通过底部的多个标签(图标+文字),实现Fragment的快速切换,是现在开发中替代自定义底部导航栏的最优选择。
通俗比喻
结合你之前的FragmentContainerView+Navigation架构,整个App的底部导航逻辑可以比作手机的底部应用栏:
BottomNavigationView:手机底部的应用快捷标签(比如微信、通讯录、发现);- 每个标签:对应App的一个核心页面Fragment(比如首页、分类、我的);
- 点击标签:自动通过Navigation组件切换FragmentContainerView中显示的Fragment,无需手动写任何切换代码。
核心优势(为什么不用自定义底部导航?)
你之前如果想做底部导航,可能需要用LinearLayout+ImageView+TextView自定义,需要手动处理选中状态、图标变色、文字变色、点击事件、Fragment切换,代码繁琐且样式不统一,而BottomNavigationView完美解决这些问题,核心优势如下:
- 原生Material风格:遵循Google官方设计规范,样式统一、视觉美观,支持圆角、阴影、选中动画(比如标签点击的缩放/变色),适配所有安卓版本;
- 内置选中状态管理:自动处理标签的「选中/未选中」状态,包括图标颜色、文字颜色、大小的切换,无需手动写判断逻辑;
- 与Navigation组件无缝联动:一行代码即可完成绑定,点击底部标签自动切换Fragment,无需手动写
onItemSelected点击事件和Fragment切换代码(你的代码就是这个核心场景); - 基于菜单资源配置:底部的所有标签(图标、文字、id)都在menu菜单资源中定义,实现「配置式开发」,无需在XML中写多个控件,修改标签只需改menu文件;
- 自动适配屏幕:支持标签数量的自适应,标签过多时会自动处理(比如隐藏文字、转为弹窗),无需手动做屏幕适配;
- 高度可自定义:支持修改选中/未选中颜色、文字大小、图标大小、导航栏背景色等,满足不同App的UI设计需求;
- 无需第三方库:属于Google官方的Material库,只需添加基础依赖即可使用,无兼容性问题。
核心使用前提
该控件是Material Design组件,使用前必须保证你的App主题继承自Material风格主题(否则会报错/样式异常),这是新手最易踩的坑,主题配置在res/values/styles.xml中,示例:
<!-- 正确:继承Material主题,推荐使用DayNight(日夜模式) -->
<style name="Theme.MvvmDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- 其他主题配置 -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorAccent">@color/teal_200</item>
</style>
✅ 只需保证主题父类是Theme.MaterialComponents.XXX即可,无需其他修改。
二、拆解你贴的BottomNavigationView完整代码(逐行解释,新手秒懂)
你贴的代码是BottomNavigationView最标准、最核心的用法(配合Navigation组件),所有属性都是开发中必用的,按「基础布局属性+Material专属核心属性」分类解释,易懂易记。
你的原始代码
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
app:menu="@menu/bottom_navigation" />
逐行属性解释
第一类:基础布局属性(你熟悉的,和普通控件一致)
<!-- 1. 给底部导航栏设置唯一id:后续代码中通过id绑定Navigation用 -->
android:id="@+id/bottom_nav_view"
<!-- 2. 宽度占满父布局:底部导航栏默认是全屏宽度 -->
android:layout_width="match_parent"
<!-- 3. 高度设为56dp:Google Material Design官方推荐的底部导航栏高度,固定值,无需修改 -->
android:layout_height="56dp"
<!-- 4. 布局对齐方式:靠底部显示(适配LinearLayout/FrameLayout/ConstraintLayout) -->
android:layout_gravity="bottom"
✅ 重点记住:layout_height="56dp"是官方标准高度,layout_gravity="bottom"保证导航栏固定在页面底部,这两个属性几乎不用修改。
第二类:BottomNavigationView 核心专属属性(1个必配,核心中的核心)
<!-- 绑定底部导航的菜单资源文件:所有标签(图标+文字+id)都在这个menu文件中定义 -->
app:menu="@menu/bottom_navigation"
- 该属性是必配项,没有它底部导航栏会是空白的;
@menu/bottom_navigation表示引用res/menu文件夹下的bottom_navigation.xml菜单资源文件;- 底部导航的每个标签对应菜单文件中的一个
<item>标签,标签的图标、文字、唯一id都在<item>中定义。
该代码的整体作用(结合FragmentContainerView)
在页面底部创建一个Material风格的底部导航栏,通过@menu/bottom_navigation加载导航标签,后续通过代码将其与上方的FragmentContainerView(Navigation)绑定,实现「点击底部标签自动切换容器中的Fragment」。
三、核心准备:添加Material库依赖(必加)
BottomNavigationView属于Google Material Components库,需在模块级build.gradle(app/build.gradle)中添加依赖,结合你之前的Fragment/Navigation/ViewBinding依赖,给出完整的依赖代码,直接复制即可,避免缺依赖报错。
完整build.gradle依赖配置(含所有之前的组件)
dependencies {
// 基础依赖
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
// ViewModel+LiveData
implementation "androidx.lifecycle:lifecycle-viewmodel:2.7.0"
implementation "androidx.lifecycle:lifecycle-livedata:2.7.0"
// FragmentContainerView 依赖
implementation "androidx.fragment:fragment:1.6.1"
// Navigation 组件依赖
implementation "androidx.navigation:navigation-fragment:2.7.6"
implementation "androidx.navigation:navigation-ui:2.7.6"
// 核心:Material Design库依赖(BottomNavigationView属于这个库,必加)
implementation 'com.google.android.material:material:1.11.0'
}
android {
compileSdk 34
buildFeatures {
viewBinding true // 保留ViewBinding,后续配合使用
}
defaultConfig {
applicationId "com.xxx.mvvm_demo" // 你的包名
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
添加后点击Sync Now同步Gradle,完成后即可创建菜单资源文件。
四、核心步骤1:创建menu菜单资源文件(定义底部导航标签)
BottomNavigationView的所有标签都在menu资源文件中定义,这是「配置式开发」的关键,新手需严格按步骤创建,保证文件路径和名称正确。
步骤1:创建menu资源文件夹
- 在Android Studio的res目录上右键 → New → Android Resource Directory;
- 在弹出的窗口中,Resource type选择menu(必须选,否则无法创建菜单文件),Directory name自动变为
menu,点击OK; - 创建完成后,res目录下会出现menu文件夹,这是存放所有菜单资源的地方。
步骤2:创建底部导航的菜单文件(bottom_navigation.xml)
- 在menu文件夹上右键 → New → Menu Resource File;
- File name填写
bottom_navigation(必须和你XML中app:menu="@menu/bottom_navigation"的名称一致),点击OK; - 自动生成
res/menu/bottom_navigation.xml文件,开始编写标签配置。
步骤3:编写menu文件(核心,定义每个底部标签)
每个底部导航标签对应一个<item>标签,必须包含android:id(唯一标识)、android:icon(标签图标)、android:title(标签文字),这三个是必配属性,示例为3个标签(首页/分类/我的),贴合实际开发。
完整bottom_navigation.xml代码(可直接复制)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 标签1:首页 -->
<item
android:id="@+id/homeFragment"
android:icon="@mipmap/ic_home"
android:title="首页" />
<!-- 标签2:分类 -->
<item
android:id="@+id/categoryFragment"
android:icon="@mipmap/ic_category"
android:title="分类" />
<!-- 标签3:我的 -->
<item
android:id="@+id/mineFragment"
android:icon="@mipmap/ic_mine"
android:title="我的" />
</menu>
关键注意事项(菜单文件核心规则,新手必记)
- android:id 必须和导航图中的Fragment id一致:这是后续和Navigation联动的核心关键(点击标签时,Navigation会根据该id找到对应的Fragment并切换);
- 比如菜单中首页的id是
@+id/homeFragment,那么res/navigation/mobile_navigation.xml中首页Fragment的id也必须是@+id/homeFragment;
- 比如菜单中首页的id是
- android:icon:设置标签的图标,建议用mipmap/ drawable资源,图标尺寸推荐24dp(Material标准);
- android:title:设置标签的文字,简洁明了(比如首页、我的、消息);
- 标签数量建议3-5个:Material Design规范中,底部导航的标签数量推荐3-4个,最多5个;超过5个会自动隐藏文字,仅显示图标;
- 无需添加选中状态:选中/未选中的图标、文字颜色由BottomNavigationView自动管理,无需在menu中配置。
五、核心步骤2:与Navigation+FragmentContainerView 一键绑定(一行代码搞定)
这是你这个代码组合的灵魂步骤,也是官方推荐的最优方式:通过NavigationUI的一行代码,将BottomNavigationView与Navigation的NavController绑定,实现「点击底部标签自动切换Fragment」,无需手动写任何点击事件和Fragment切换代码。
核心原理
NavController:Navigation组件的导航控制器,由FragmentContainerView中的NavHostFragment提供,负责管理Fragment的跳转/切换;NavigationUI.setupWithNavController():官方提供的绑定方法,传入BottomNavigationView和NavController,自动完成:- 监听底部导航栏的标签点击事件;
- 根据标签的id,匹配导航图中对应的Fragment并切换;
- 自动更新底部导航栏的选中状态(点击哪个标签,哪个标签高亮);
- 配合
app:defaultNavHost="true",自动处理返回键逻辑。
实现步骤(2步,核心代码在主Activity中)
你的主Activity布局中包含「FragmentContainerView + BottomNavigationView」,只需在Activity的onCreate中完成获取NavController+绑定导航栏,全程仅需2行核心代码,结合ViewBinding实现(你已掌握ViewBinding)。
完整主Activity代码(Java+ViewBinding+绑定核心)
import com.xxx.mvvm_demo.databinding.ActivityMainBinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding; // ViewBinding,自动生成的布局绑定类
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. ViewBinding加载布局(替代setContentView,无findViewById)
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// 2. 核心步骤1:获取Navigation的导航控制器NavController
// 从FragmentContainerView中获取NavHostFragment,再获取NavController
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
.findFragmentById(R.id.nav_host_fragment_content_main);
NavController navController = navHostFragment.getNavController();
// 3. 核心步骤2:一键绑定BottomNavigationView和NavController(仅这1行!)
NavigationUI.setupWithNavController(binding.bottomNavView, navController);
}
}
代码关键说明
- 获取NavController的方式:必须从
FragmentContainerView的id(nav_host_fragment_content_main)中获取NavHostFragment,再通过它得到NavController,这是固定写法,无需修改; - 绑定方法:
NavigationUI.setupWithNavController(底部导航栏对象, 导航控制器),其中binding.bottomNavView是ViewBinding自动生成的底部导航栏对象(对应XML中的bottom_nav_view); - 主Activity无其他代码:绑定完成后,无需写任何点击事件、Fragment切换代码,所有逻辑由Navigation和BottomNavigationView自动处理;
- 导航图配合:确保
res/navigation/mobile_navigation.xml中:- 每个Fragment的
android:id和menu文件中标签的android:id完全一致; app:startDestination设置为默认显示的Fragment(比如首页homeFragment),App启动后会自动显示该Fragment,且底部导航栏对应标签自动选中。
- 每个Fragment的
运行效果(绑定成功后)
- App启动后,FragmentContainerView显示导航图中
startDestination的Fragment(比如首页),底部导航栏的「首页」标签自动高亮; - 点击底部导航栏的「分类」标签,容器中自动切换为分类Fragment,「分类」标签高亮,首页标签恢复未选中状态;
- 点击返回键,自动回退到上一个Fragment(配合
app:defaultNavHost="true"),底部导航栏选中状态同步更新; - 所有切换过程自带Material风格的淡入淡出动画,无需手动配置。
六、常用自定义样式(修改底部导航栏的外观)
默认的底部导航栏样式可能和你的AppUI不符,可通过XML属性修改选中颜色、未选中颜色、背景色、文字大小等,无需写Java代码,直接在BottomNavigationView的XML标签中添加属性即可,常用自定义属性如下:
自定义核心属性(直接添加到你的BottomNavigationView标签中)
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
app:menu="@menu/bottom_navigation"
<!-- 1. 修改底部导航栏的背景色(比如白色、主色) -->
app:itemBackground="@color/white"
<!-- 2. 修改标签选中时的图标+文字颜色(比如App主色) -->
app:itemIconTint="@color/bottom_nav_selected"
app:itemTextColor="@color/bottom_nav_selected"
<!-- 3. 自定义选中/未选中的颜色(推荐:用colorSelector资源,更灵活) -->
app:itemIconTint="@color/bottom_nav_color_selector"
app:itemTextColor="@color/bottom_nav_color_selector"
<!-- 4. 修改标签文字的大小(单位:sp) -->
app:itemTextSize="12sp"
<!-- 5. 修改图标大小(单位:dp,Material标准24dp) -->
app:itemIconSize="24dp" />
推荐:用ColorSelector实现选中/未选中颜色切换(更灵活)
直接设置单一颜色只能固定一种,用颜色选择器(ColorSelector) 可实现「选中时显示主色,未选中时显示灰色」,步骤如下:
- 在
res/color目录下新建bottom_nav_color_selector.xml(无color目录则新建,Resource type选color); - 编写颜色选择器代码:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态的颜色(App主色) -->
<item android:color="@color/purple_500" android:state_checked="true"/>
<!-- 未选中状态的颜色(灰色) -->
<item android:color="@color/gray_600" android:state_checked="false"/>
</selector>
- 在BottomNavigationView中引用该颜色选择器(如上面的属性示例),即可实现自动的颜色切换。
七、BottomNavigationView 新手常见坑 & 解决方案
结合你的使用场景(配合Navigation),整理新手最易踩的6个坑,附具体解决方案,避免运行报错/样式异常/功能失效。
坑1:运行APP崩溃,报错「IllegalStateException: This Activity already has an action bar supplied by the window decor」
原因:App主题继承了带ActionBar的主题(比如Theme.AppCompat.Light.DarkActionBar),而Material的BottomNavigationView和ActionBar冲突;
解决方案:将App主题改为Material无ActionBar主题,修改res/values/styles.xml:
<!-- 推荐:无ActionBar的Material主题,避免冲突 -->
<style name="Theme.MvvmDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorAccent">@color/teal_200</item>
</style>
坑2:底部导航栏是空白的,无任何标签
原因:
- 未绑定
app:menu属性,或menu文件路径/名称写错; - menu文件中没有
<item>标签,或<item>标签无icon/title;
解决方案: - 确保添加
app:menu="@menu/bottom_navigation",且menu文件在res/menu下,名称一致; - 确保menu文件中有至少1个
<item>标签,且包含android:id/android:icon/android:title。
坑3:点击底部标签,Fragment不切换,无任何反应
原因:
- menu文件中标签的
android:id和导航图中Fragment的android:id不一致; - 未调用
NavigationUI.setupWithNavController()完成绑定; - 获取NavController时,FragmentContainerView的id写错;
解决方案: - 严格保证menu标签id = 导航图Fragment id(完全一致,包括大小写);
- 检查Activity中是否调用了绑定方法,且代码无报错;
- 确认获取NavController的id是FragmentContainerView的真实id(你的是
nav_host_fragment_content_main)。
坑4:底部导航栏的选中状态不更新(点击标签,Fragment切换但标签未高亮)
原因:手动通过Navigation.findNavController().navigate()跳转Fragment,而非通过底部标签点击,导致选中状态不同步;
解决方案:
- 尽量通过底部标签点击切换Fragment,避免手动跳转;
- 若需手动跳转,跳转后调用
navController.popBackStack()清空回退栈,保证选中状态同步。
坑5:标签数量超过3个,文字被隐藏,仅显示图标
原因:Material Design规范中,底部导航栏标签数量≥4个时,默认会「选中标签显示文字+图标,未选中标签仅显示图标」,节省空间;
解决方案:若需所有标签都显示文字+图标,在Activity中添加一行代码:
// 设置所有标签始终显示文字+图标(标签数量≥4时用)
binding.bottomNavView.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED);
✅ 需导入com.google.android.material.bottomnavigation.LabelVisibilityMode。
坑6:BottomNavigationView 样式异常(比如无圆角、无阴影)
原因:
- App主题未继承Material主题;
- Material库依赖版本过低;
解决方案: - 确保主题父类是
Theme.MaterialComponents.XXX; - 将Material库依赖升级到最新稳定版(比如
1.11.0)。
八、核心总结:FragmentContainerView + Navigation + BottomNavigationView 完整架构
你贴的代码是安卓官方推荐的单Activity多Fragment底部导航经典架构,也是现在企业开发的主流标准写法,三者分工明确、无缝配合,形成一个完整的、低耦合的、易维护的App基础架构,总结三者的核心分工:
- FragmentContainerView:Fragment的专属容器,负责承载所有Fragment的显示,替代传统FrameLayout,避免生命周期/重叠问题;
- Navigation:Fragment的导航管理器,通过导航图(
mobile_navigation.xml)定义Fragment的列表和跳转规则,负责Fragment的切换、回退、传值; - BottomNavigationView:底部导航交互入口,通过menu菜单资源定义底部标签,与Navigation一键绑定后,成为Fragment切换的可视化入口,负责接收用户的底部标签点击操作。
这套架构的核心优势(新手必懂)
- 无繁琐的Fragment管理代码:无需手动写
FragmentManager/FragmentTransaction,无需写底部导航的点击事件,一行代码完成绑定; - 低耦合:Fragment之间相互独立,跳转规则由导航图配置,修改跳转逻辑只需改XML,无需改Java代码;
- 易维护:底部标签由menu文件配置,Fragment由导航图配置,UI和逻辑分离,后续新增/删除标签/Fragment只需改配置文件;
- 样式统一:BottomNavigationView是Material风格,自带选中状态、动画,无需自定义,保证App的视觉一致性;
- 生命周期安全:FragmentContainerView严格遵循Fragment生命周期,Navigation自动管理Fragment事务,避免内存泄漏/数据丢失。
后续学习路线(循序渐进,基于该架构)
掌握这套核心架构后,可在其基础上循序渐进学习以下内容,覆盖实际开发的所有核心场景:
- Fragment之间的安全传值:通过Navigation的Bundle或ViewModel实现Fragment之间的传值(避免手动传值的空指针);
- BottomNavigationView的高级自定义:实现圆角底部导航、添加角标(消息未读数)、修改选中动画等;
- Navigation的深层链接:实现从App外部(比如浏览器、通知)跳转到App内的指定Fragment;
- 结合ViewModel+LiveData:在每个Fragment中使用ViewModel+LiveData处理业务逻辑和数据,实现「数据驱动UI」;
- ViewPager2 + TabLayout:在单个Fragment中实现顶部标签+左右滑动的子Fragment(比如首页的轮播/分类子页面)。
记住:BottomNavigationView是这套底部导航架构的可视化交互入口,其核心价值是配合Navigation实现「配置式的Fragment切换」,掌握它后,你就彻底告别了传统的自定义底部导航和繁琐的Fragment管理,为开发中大型App打下坚实的基础。
更多推荐




所有评论(0)