前言: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 的流畅性和一致性。