前言:Launcher3中底部Taskbar和Navbar,或者说中文里面的术语导航栏,这几个词是很容易让人混淆的地方。本文要介绍的是Taskbar。从字面上意思来看,Taskbar就是任务栏,任务栏是Launcher3中一个重要的组件,尤其是在大屏平板设备上。
Taskbar的UI 形态
Taskbar在Launcher3中到底是指哪部分?
直接上图:



在屏幕底部显示的带有应用图标的部分就是Taskbar中的一部分,从上面LayoutInspector中可以看到,底部高度为161dp的View就是Taskbar。
在Launcher中,Taskbar的视图类是TaskbarDragLayer。
Taskbar有两种状态,一种是上图中完全展开的形态,有个术语叫UnStash状态,还有一种是只有底部一个bar条的形态,这种叫Stash状态。

Taskbar的显示逻辑与流程
核心目标:
Taskbar 旨在为大屏幕设备(平板电脑、折叠屏)提供类似桌面操作系统的体验,在应用运行时提供一个常驻或可按需访问的应用启动器和导航栏。
关键组件和控制器:
-
TaskbarManager.java: 这是 Taskbar 的全局管理器。- 职责: 决定是否应该在当前设备和配置下启用 Taskbar。监听设备状态变化(如屏幕旋转、折叠状态、导航模式切换)来判断是否需要创建或销毁 Taskbar UI。
- 流程: 当
TaskbarManager确定需要 Taskbar 时(通常在LauncherActivity或类似上下文启动时),它会创建TaskbarActivityContext。
-
TaskbarActivityContext.java: Taskbar 的上下文环境。- 职责: 这是 Taskbar UI 实际运行的 Context。它负责创建 Taskbar 的主视图(
TaskbarView)和 DragLayer (TaskbarDragLayer),并初始化所有的 Taskbar 控制器 (TaskbarControllers)。它还处理与 Launcher Activity 的生命周期绑定、资源获取、权限检查等。 - 流程:
- 在
onCreate()中,它会加载 Taskbar 布局 (R.layout.taskbar)。 - 创建
TaskbarControllers实例,该实例会聚合所有其他的子控制器。 - 调用
TaskbarControllers.init()来初始化所有子控制器,并建立它们之间的引用。
- 在
- 职责: 这是 Taskbar UI 实际运行的 Context。它负责创建 Taskbar 的主视图(
-
TaskbarControllers.java: 控制器聚合器。- 职责: 持有所有其他 Taskbar 子控制器的引用,方便它们之间相互访问和协调。提供一个统一的初始化 (
init) 和销毁 (onDestroy) 入口。 - 流程: 在
init()中,它会按顺序创建并初始化各个子控制器,如TaskbarViewController、TaskbarStashController、NavbarButtonsViewController等。
- 职责: 持有所有其他 Taskbar 子控制器的引用,方便它们之间相互访问和协调。提供一个统一的初始化 (
-
TaskbarView.java: 核心 UI 视图。- 职责: 继承自
ViewGroup,是容纳 Taskbar 上所有图标(应用、文件夹、预测应用)的容器。它处理图标的布局、添加、移除和更新。 - 显示: 由
TaskbarActivityContext创建并添加到其TaskbarDragLayer中。
- 职责: 继承自
-
TaskbarViewController.java: TaskbarView 的控制器。- 职责: 管理
TaskbarView的内容和视觉状态。- 从数据模型(通过
TaskbarModelCallbacks)获取应用列表并绑定到TaskbarView上。 - 处理图标的点击、长按(弹出菜单,通过
TaskbarPopupController)。 - 控制图标的 Alpha、Scale、TranslationY 等属性,用于配合
TaskbarStashController实现收起/展开动画。
- 从数据模型(通过
- 流程: 在
TaskbarControllers.init()中被初始化,并获取对TaskbarView的引用。监听数据模型变化来更新 UI。
- 职责: 管理
-
TaskbarStashController.java: 收起/展开逻辑的核心控制器。- 职责: 这是控制 Taskbar 是否应该显示为完整条、收起成一个细条 (Handle) 或完全隐藏的决策中心和动画协调者。
- 原理 (State Flags): 维护一个整数状态
mState,通过位标志 (FLAG_*) 来记录各种影响 Taskbar 可见性的条件,例如:FLAG_IN_APP: 是否在第三方应用中。FLAG_STASHED_IN_APP_SYSUI: 系统 UI(如通知面板)是否要求收起。FLAG_STASHED_IN_APP_IME: 输入法是否显示。FLAG_IN_STASHED_LAUNCHER_STATE: 当前 Launcher 状态是否要求收起 (如 AllApps)。FLAG_STASHED_IN_APP_AUTO: 是否是瞬态 Taskbar (Transient) 且当前处于自动隐藏状态。FLAG_STASHED_DEVICE_LOCKED: 设备是否锁定。- ... 等等。
- 决策 (
mIsStashedPredicate): 通过一个IntPredicate(一个函数式接口,输入 int 返回 boolean) 来判断当前mState的组合是否意味着 Taskbar 应该 处于收起状态 (mIsStashed)。 - 状态应用 (
applyState,createApplyStateAnimator): 当外部事件(如 Launcher 状态改变、系统 UI 状态改变、用户交互等)调用updateStateForFlag()更新了mState后,会调用applyState()。applyState()会比较新的预期收起状态 (mIsStashedPredicate.test(newState)) 和当前的视觉状态 (mIsStashed)。- 如果状态不一致,则调用
createAnimToIsStashed()创建一个AnimatorSet来驱动 Taskbar UI 元素的动画(背景、图标、Handle 的 Alpha、Scale、Translation)。 - 动画会平滑地过渡到新的收起/展开状态。
- 如果状态不一致,则调用
- 动画类型 (
@StashAnimation): 支持多种不同的收起/展开动画效果(TRANSITION_DEFAULT,TRANSITION_HOME_TO_APP,TRANSITION_HANDLE_FADE等),根据触发场景选择不同的动画插值器和时序。 - 瞬态 Taskbar (Transient): 对瞬态 Taskbar 有特殊处理逻辑,包括自动隐藏计时器 (
mTimeoutAlarm) 和通过updateAndAnimateTransientTaskbar()进行显式控制。
-
StashedHandleViewController.java&StashedHandleView.java: 收起状态 Handle 的控制器和视图。- 职责: 当
TaskbarStashController决定收起 Taskbar 时,StashedHandleViewController负责显示和管理StashedHandleView(那个白色细条)。它控制 Handle 的 Alpha 和 Scale,实现显示/隐藏以及 Hint 动画(轻微放大提示可以展开)。
- 职责: 当
-
TaskbarLauncherStateController.java: Launcher 状态转换器。- 职责: 监听 Launcher 的状态变化(
StateManager.addStateListener),并将 Launcher 状态(如NORMAL,OVERVIEW,ALL_APPS)转换为TaskbarStashController能理解的状态标志 (FLAG_IN_STASHED_LAUNCHER_STATE,FLAG_IN_APP,FLAG_IN_OVERVIEW)。 - 流程: 当 Launcher 状态切换时,它会调用
TaskbarStashController.updateStateForFlag()来更新相应的标志,然后触发TaskbarStashController.applyState()来应用变化。
- 职责: 监听 Launcher 的状态变化(
-
TaskbarInsetsController.kt: 窗口 Insets 控制器。- 职责: 根据 Taskbar 的收起/展开状态和类型(持久/瞬态),计算应该向应用报告的窗口 Insets(底部导航栏区域)。
- 原理: 当 Taskbar 展开时,报告完整的高度;当收起时,通常报告
mStashedHeight(Handle 的高度),或者在某些特定情况(如持久 Taskbar + IME)下报告 0。它确保应用的内容能够正确地避让 Taskbar。
-
TaskbarStashViaTouchController.kt: 触摸交互控制器。- 职责: 处理用户直接在 Stashed Handle 或 Taskbar 背景区域的触摸事件,用于触发/取消瞬态 Taskbar 的自动隐藏计时器,或者启动 Hint 动画。
-
其他控制器:
NavbarButtonsViewController.java: 管理导航按钮(Back, Home, Recents)的显示、布局和点击事件。TaskbarDragLayerController.java: 管理 Taskbar 的背景、圆角和 Y 轴偏移。TaskbarDragController.java: 处理 Taskbar 内部或涉及 Taskbar 的拖放操作。TaskbarKeyguardController.java: 处理锁屏状态对 Taskbar 的影响。TaskbarPinningController.kt: 处理用户固定/取消固定持久 Taskbar 的设置。TaskbarAutohideSuspendController.java: 管理自动隐藏功能的暂停状态。- ... 等等。
加载与显示流程总结:
- 启动:
LauncherActivity(或其他宿主) 启动。 TaskbarManager决策:TaskbarManager判断当前设备配置是否需要 Taskbar。- 创建 Context: 如果需要,
TaskbarManager创建TaskbarActivityContext。 - Context 初始化:
TaskbarActivityContext加载布局,创建TaskbarDragLayer和TaskbarView。 - 控制器聚合:
TaskbarActivityContext创建TaskbarControllers。 - 子控制器初始化:
TaskbarControllers.init()逐个创建并初始化所有子控制器 (TaskbarViewController,TaskbarStashController,NavbarButtonsViewController等),并建立它们之间的引用。 - 初始状态设定:
TaskbarStashController根据初始条件(是否 Setup、是否 Phone Mode、是否瞬态、持久化设置等)设置初始的mState标志。TaskbarLauncherStateController获取当前的 Launcher 状态,并更新TaskbarStashController的相应标志。
- 首次应用状态:
TaskbarStashController.applyState(0)被调用,根据初始的mState计算出 Taskbar 应该是收起还是展开,并立即设置 UI 到对应的视觉状态(没有动画)。 - Insets 更新:
TaskbarInsetsController根据初始状态计算并向系统报告窗口 Insets。 - 数据绑定:
TaskbarViewController开始从模型加载数据并绑定到TaskbarView上显示图标。 - 事件监听与状态更新:
TaskbarLauncherStateController监听 Launcher 状态变化。TaskbarStashController监听系统 UI 状态变化 (updateStateForSysuiFlags)。TaskbarKeyguardController监听锁屏状态。TaskbarPinningController监听用户 pinning 设置。TaskbarStashViaTouchController监听触摸事件。- 当任何影响可见性的状态发生变化时,对应的控制器会调用
TaskbarStashController.updateStateForFlag()。
- 动画应用:
TaskbarStashController.applyState()被调用,如果计算出的收起/展开状态与当前视觉状态不同,则创建并启动动画 (mAnimator) 来平滑过渡。 - 持续运行: Taskbar 进入运行状态,响应用户交互(点击、长按、拖拽)、系统事件和状态变化,并由
TaskbarStashController动态调整其可见性。
核心原理:
Taskbar 的核心原理是基于状态的可见性控制 和动画协调。
- 状态驱动:
TaskbarStashController通过维护一组状态标志 (mState) 来集中管理所有影响 Taskbar 是否应该收起的因素。 - 解耦: 每个子控制器负责监听和更新自己相关的状态标志,将具体逻辑与最终的可见性决策解耦。
- 集中决策:
TaskbarStashController根据所有标志的组合,使用mIsStashedPredicate做出最终的收起/展开决策。 - 动画协调:
TaskbarStashController负责创建和管理收起/展开的AnimatorSet,该动画集会同时驱动 Taskbar 背景、图标和 Handle 等多个 UI 元素的属性变化,实现协调一致的视觉过渡。 - Insets 同步:
TaskbarInsetsController确保窗口 Insets 与 Taskbar 的状态(以及动画预期)保持一致,让应用能够正确布局。
这种设计使得 Taskbar 能够灵活地响应各种复杂的系统和用户状态变化,同时保持 UI 的流畅性和一致性。