AppBar与菜单

AppBar与菜单:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。

学习目标

  • 能沿“AppBar与菜单”的用户事件解释Android组件、状态所有者、线程与销毁边界。
  • 能围绕“掌握 Toolbar 替代 ActionBar——菜单资源、Options Menu、Overflow 和 Navigation Icon 的配置。”改出一个可运行结果,并用前后状态而非组件数量验收。
  • 能在旋转、进程重建、拒权、离线或无效输入中选择适用反例,定位首个状态分叉。
  • 能用双视口、大字体、焦点顺序、状态语义和对比度截图独立重放结论,并标明第四版机制与现代targetSdk政策的边界。

顶栏不只是标题——它是命令中枢

(Toolbar)承载标题、返回箭头和「更多」菜单。Fragment 时代默认

不够灵活——把 Toolbar 放进布局,才能和 CoordinatorLayout、Drawer 配合。

启用 Toolbar

res/layout/activity_main.xml

<com.google.android.material.appbar.MaterialToolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    app:title="@string/app_name" />
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar?.setDisplayHomeAsUpEnabled(true)

主题使用 NoActionBar 变体,避免双顶栏。

Options Menu

res/menu/crime_list_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menu_add"
        android:icon="@drawable/ic_add"
        android:title="@string/add_crime"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/menu_settings"
        android:title="@string/settings"
        app:showAsAction="never" />
</menu>
override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.crime_list_menu, menu)
    return true
}
 
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.menu_add -> { viewModel.addCrime(); true }
        R.id.menu_settings -> { startActivity(Intent(this, SettingsActivity::class.java)); true }
        android.R.id.home -> { onBackPressedDispatcher.onBackPressed(); true }
        else -> super.onOptionsItemSelected(item)
    }
}

showAsAction="ifRoom" 有空间时显示图标;never 进 Overflow(⋮)。

AppBar(Toolbar)拆开看:导航 · 标题 · action · 溢出菜单菜单项在 menu XML 里声明,showAsAction 决定它落在栏上还是收进 ⋮犯罪记录+导航图标返回 ← / 汉堡 ☰标题 title当前界面名action 按钮高频项,图标直接上栏溢出菜单 ⋮点开看其余项菜单是怎么来的、怎么落位、点击怎么处理onCreateOptionsMenu(menu, inflater)加载 res/menu/*.xml 填充菜单app:showAsAction="ifRoom|always|never"决定该项显示在栏上还是收进溢出菜单onOptionsItemSelected(item)按 item.itemId 分发点击;处理完 return true⋮ 溢出菜单(点开后)设置showAsAction="never"分享栏上放不下,收进来关于低频项默认进溢出
AppBar(Toolbar)从左到右是导航图标、标题、若干 action 图标和溢出入口 。菜单项统一在 res/menu/*.xml 里声明,由 onCreateOptionsMenu 加载;app:showAsActionifRoom/always/never)决定它显示在栏上还是收进 ;点击最终都回到 onOptionsItemSelecteditemId 分发处理。
分步1 / 4

① 列表页配置 Toolbar

在列表 Activity 的布局中放置 MaterialToolbar,主题使用 NoActionBar 变体避免双顶栏。通过 setSupportActionBar(toolbar) 将 Toolbar 设为 Activity 的 App Bar——列表页的 Toolbar 通常只显示标题和菜单项(如"添加"),不显示返回箭头(home 图标)。

小结

  • NoActionBar 主题 + 布局 Toolbar + setSupportActionBar
  • menu XML + inflate + onOptionsItemSelected
  • Home/Up 与返回栈一致;Navigation 组件可自动绑定

练习

问题 1:“第14章 The App Bar”覆盖哪些正式节点和项目主线?

问题 2:怎样建立本页最小可执行实验?

问题 3:为什么只在正常点击路径运行不能证明完成?

问题 4:怎样设计能推翻当前实现的反例?

问题 5:从第4版语境迁移到现代目标SDK时如何控制变量?

问题 6:本页达到独立交接标准需要什么?

名词解释

本章出现的专业名词,用大白话再讲一遍。

App Bar

Material Design 顶栏,可显示标题、导航图标和操作菜单,现代 App 多用 Toolbar 实现。

ActionBar

旧版框架提供的顶栏,现推荐用 Toolbar + setSupportActionBar 替代。

Toolbar

可嵌入布局的顶栏控件,功能同 ActionBar 但可定制位置与样式。

Options Menu

Activity/Fragment 顶栏右侧的操作项集合,由 menu 资源定义。

Up navigation

应用内层级返回(←),不同于系统 Back——应弹出当前 Task 内上一级 Destination。

“AppBar与菜单”不使用未获授权的纸书正文;InformIT出版信息授权电子版完整目录只用于确认第四版32章、269个正式目录节点和时代语境,第四版官方勘误用于识别工具链变更。下列中文解释、图示、交互、代码与练习均为独立教学重写,平台行为再以Android Developers的一手文档复核。

为什么“AppBar与菜单”必须回到可观察状态

“AppBar与菜单”的学习结果不是记住类名,而是能预测“掌握 Toolbar 替代 ActionBar——菜单资源、Options Menu、Overflow 和 Navigation Icon 的配置。”在一次输入、一次重建和一次失败中的不同状态,并指出哪条Android合同产生差异。

第四版机制逐项深读

14. The App Bar

在“AppBar与菜单”中,“14. The App Bar”把顶层动作、导航和溢出菜单接入当前界面状态;菜单创建与选择回调要在窄屏、无数据和权限受限时仍有可理解反馈。

AppCompat Default App Bar

在“AppBar与菜单”中,“AppCompat Default App Bar”把顶层动作、导航和溢出菜单接入当前界面状态;菜单创建与选择回调要在窄屏、无数据和权限受限时仍有可理解反馈。

在“AppBar与菜单”中,“Menus”把顶层动作、导航和溢出菜单接入当前界面状态;菜单创建与选择回调要在窄屏、无数据和权限受限时仍有可理解反馈。

Using the Android Asset Studio

在“AppBar与菜单”中,“Using the Android Asset Studio”通过资源限定符和主题属性把内容与设备配置解耦;最终值由资源匹配和主题继承共同决定。

For the More Curious: App Bar vs Action Bar vs Toolbar

在“AppBar与菜单”中,“For the More Curious: App Bar vs Action Bar vs Toolbar”把顶层动作、导航和溢出菜单接入当前界面状态;菜单创建与选择回调要在窄屏、无数据和权限受限时仍有可理解反馈。

For the More Curious: Accessing the AppCompat App Bar

在“AppBar与菜单”中,“For the More Curious: Accessing the AppCompat App Bar”把顶层动作、导航和溢出菜单接入当前界面状态;菜单创建与选择回调要在窄屏、无数据和权限受限时仍有可理解反馈。

Challenge: An Empty View for the RecyclerView

在“AppBar与菜单”中,验证“Challenge: An Empty View for the RecyclerView”用插入、删除、重排和多类型数据检查item身份、焦点与点击目标,记录首个错误绑定。

“AppBar与菜单”验收回顾

“AppBar与菜单”只有在双视口、大字体、焦点顺序、状态语义和对比度截图能够从相同基线再次得到相同断言时才通过;第四版机制与现代平台政策分别记录,不用新API名称掩盖旧行为。

← 上一页:对话框 · 下一页:隐式intent →

章专属可重放状态实验

先预测“创建菜单、选择动作、数据变空与配置重建”发生后,MenuHost、当前 Fragment 与列表状态应怎样改变菜单可见性、动作启用、空列表和导航结果;再操作三个实验。第四版示例与当前 Android 政策分别记录,实验不把新 API 名称倒填为原书内容。

实验一:所有者—状态—结果合同

选择任一正式目录节点和正常/边界场景,检查它是否真的进入本章状态合同。目录标题只有同时出现在解释、可视状态和交付证据中才算覆盖。

Owner · state · observable result

AppBar与菜单:状态合同

把 CriminalIntent 的新增、删除、搜索和导航动作接入当前界面状态

验证场景

第四版正式目录节点

app-bar · 正常任务

14. The App Bar固定 SDK、设备配置和初始状态,触发“创建菜单、选择动作、数据变空与配置重建”

状态所有者MenuHost、当前 Fragment 与列表状态
受控状态菜单可见性、动作启用、空列表和导航结果
触发事件创建菜单、选择动作、数据变空与配置重建

冻结入口:14. The App Bar

记录MenuHost、当前 Fragment 与列表状态的初始菜单可见性、动作启用、空列表和导航结果

观察:menu item、目的地 ID、动作日志、列表快照和导航断言中的“14. The App Bar”轨迹

预期:由MenuHost、当前 Fragment 与列表状态提交菜单可见性、动作启用、空列表和导航结果,并持续满足“菜单由当前状态派生,不能对已销毁目的地继续执行动作”

实验二:事件与生命周期轨迹

沿五次转换逐步执行“创建菜单、选择动作、数据变空与配置重建”。每一步只允许MenuHost、当前 Fragment 与列表状态按职责提交状态,并持续核对“菜单由当前状态派生,不能对已销毁目的地继续执行动作”。

Deterministic event replay

AppBar与菜单:事件轨迹

选择一次状态转换1 / 5

不变量:菜单由当前状态派生,不能对已销毁目的地继续执行动作

交付证据:menu item、目的地 ID、动作日志、列表快照和导航断言

实验三:章专属反例与同输入恢复

注入“返回列表后旧详情 Fragment 仍处理删除菜单,删错 Crime”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有menu item、目的地 ID、动作日志、列表快照和导航断言一起恢复才算修复。

Fault · cancel · restore

AppBar与菜单:反例与恢复

故障:返回列表后旧详情 Fragment 仍处理删除菜单,删错 Crime

1. 冻结输入一致

第 1 次使用相同 SDK、设备配置、初始状态与用户事件

2. 注入边界一致

保持正常输入不变,仅注入“返回列表后旧详情 Fragment 仍处理删除菜单,删错 Crime”

3. 检查所有者一致

菜单由当前状态派生,不能对已销毁目的地继续执行动作

4. 核对结果一致

menu item、目的地 ID、动作日志、列表快照和导航断言

讨论

评论区加载中…