位置:首页 > Kotlin > SystemUI开发:四张图看懂NotificationStackScrollLayout工作机制解析

SystemUI开发:四张图看懂NotificationStackScrollLayout工作机制解析

时间:2026-08-20  |  作者:穿越地图的猫  |  阅读:0

0x00. 核心组件

1、组件职责

  • NotificationStackScrollLayout: 主控制器,协调所有组件
  • AmbientState: 状态中心,存储和提供当前的环境状态
  • StackScrollAlgorithm: 计算引擎,根据状态计算布局
  • StackStateAnimator: 动画执行器,让状态变化平滑过渡
  • ExpandableViewState: 视图状态(ViewState),存储了通知在某一时刻应该具备的所有状态

2、通知容器

NotificationStackScrollLayout(NSSL)这个控制器可不简单,它得负责协调所有组件的工作呢。比如说,它要接收各种系统事件,像滑动啦、状态切换啦这些,然后把这些数据填充到 AmbientState 里面,接着还要调用计算引擎 StackScrollAlgorithm,为每一条通知算出对应的 ExpandableViewState 坐标。

SystemUI 开发之四张图看懂NotificationStackScrollLayout工作机制(九)

3、 AmbientStateExpandableViewState

  • AmbientState(输入): 它是一个单例对象(在 NSSL 生命周期内),记录了当前屏幕的宏观状态。比如:用户滚到了哪里?QS(快捷设置)拉下来了多少?现在是不是在 AOD(息屏显示)模式?哪些通知正在被拖拽?
  • ExpandableViewState(输出): 每一条通知(ExpandableNotificationRow)都有一个属于自己的 ViewState。它描述了**微观属性,**是每个通知 ExpandableNotificationRow 的目标状态。比如:这行通知应该在 Y 轴什么位置?它的透明度是多少?它的高度现在该是多少?

4、计算引擎

  • StackScrollAlgorithm.resetViewStates(AmbientState) 是计算的入口方法,它的输入参数就是AmbientState ,计算结果输出到每条通知ExpandableNotificationRow对应的ExpandableViewState 对象中

5、动画执行

StackStateAnimator 会遍历这些 ExpandableViewState。它不仅知道目标值是多少,还知道 View 当前值是多少。如果两者不一致,它就会启动属性动画(Property Animator),让 View 动起来。

0x01. 组件关系图

展示整体架构和组件间的持有关系

graph TB
    subgraph "NotificationStackScrollLayout 通知堆栈滚动布局"
        NSSL[NotificationStackScrollLayout
主容器ViewGroup] subgraph "子View管理" Children[ExpandableNotificationRow
通知行] Shelf[NotificationShelf
底部架子] Footer[FooterView
底部视图] Empty[EmptyShadeView
空白视图] end subgraph "核心组件" AS[AmbientState
环境状态管理器] SSA[StackScrollAlgorithm
布局算法引擎] StateAnimator[StackStateAnimator
状态动画执行器] end subgraph "辅助组件" SwipeHelper[NotificationSwipeHelper
滑动处理] ExpandHelper[ExpandHelper
展开手势] RoundnessManager[NotificationRoundnessManager
圆角管理] SectionsManager[NotificationSectionsManager
分组管理] end end %% NSSL 持有核心组件 NSSL -->|持有实例| AS NSSL -->|持有实例| SSA NSSL -->|持有实例| StateAnimator %% NSSL 持有辅助组件 NSSL -->|持有实例| SwipeHelper NSSL -->|持有实例| ExpandHelper NSSL -->|持有实例| RoundnessManager NSSL -->|持有实例| SectionsManager %% NSSL 管理子View NSSL -->|addView/removeView| Children NSSL -->|管理| Shelf NSSL -->|管理| Footer NSSL -->|管理| Empty %% AmbientState 的作用 AS -->|提供状态给| SSA AS -->|提供状态给| StateAnimator AS -->|提供状态给| Shelf %% 数据流向 NSSL -->|1.更新状态| AS SSA -->|2.读取状态| AS SSA -->|3.计算ViewState| Children Children -->|4.存储状态| ViewState[ExpandableViewState] StateAnimator -->|5.执行动画| ViewState ViewState -->|6.应用到| Children style NSSL fill:#e1f5ff style AS fill:#fff4e1 style SSA fill:#e8f5e8 style StateAnimator fill:#ffe8f5

0x02. 时序图

展示完整的布局更新流程中各组件的交互顺序

sequenceDiagram
    participant NSSL as NotificationStackScrollLayout
    participant AS as AmbientState
    participant SSA as StackScrollAlgorithm
    participant Animator as StackStateAnimator
    participant Row as ExpandableNotificationRow
    participant VS as ExpandableViewState

    Note over NSSL: 用户操作触发(滚动/添加/展开等)

    NSSL->>NSSL: requestChildrenUpdate()
    activate NSSL

    NSSL->>NSSL: 注册 OnPreDrawListener

    Note over NSSL: 等待下一帧 PreDraw

    NSSL->>NSSL: onPreDraw() → updateChildren()

    NSSL->>AS: 更新状态
setScrollY/setExpandFraction/etc activate AS AS-->>NSSL: 状态已更新 deactivate AS NSSL->>SSA: resetViewStates(mAmbientState) activate SSA SSA->>AS: 读取环境状态
getScrollY/getStackTranslation/etc activate AS AS-->>SSA: 返回状态值 deactivate AS loop 遍历每个子View SSA->>Row: 获取 ViewState Row-->>SSA: 返回 ExpandableViewState SSA->>SSA: 计算位置、高度、透明度等 SSA->>VS: 更新 ViewState
yTranslation/alpha/height/etc end SSA-->>NSSL: 所有 ViewState 已计算 deactivate SSA alt 需要动画 NSSL->>NSSL: startAnimationToState() NSSL->>NSSL: generateAllAnimationEvents() Note over NSSL: 生成动画事件列表 NSSL->>Animator: startAnimationForEvents(events) activate Animator loop 每个动画事件 Animator->>VS: 读取目标状态 Animator->>Animator: 创建属性动画 Animator->>Row: 应用动画
setTranslationY/setAlpha/etc end Note over Animator: 动画执行中... Animator-->>NSSL: onAnimationFinished() deactivate Animator else 不需要动画 NSSL->>NSSL: applyCurrentState() loop 遍历每个子View NSSL->>Row: applyViewState() Row->>VS: 读取状态 Row->>Row: 直接设置属性
setTranslationY/setAlpha/etc end end NSSL->>NSSL: updateBackground()
updateViewShadows() deactivate NSSL Note over NSSL,Row: 布局更新完成,等待下一次更新

0x03. 类图

展示各类的属性、方法以及类之间的依赖/组合/关联关系

classDiagram
    class NotificationStackScrollLayout {
        -AmbientState mAmbientState
        -StackScrollAlgorithm mStackScrollAlgorithm
        -StackStateAnimator mStateAnimator
        -int mOwnScrollY
        -boolean mNeedsAnimation
        -ArrayList~AnimationEvent~ mAnimationEvents

        +addContainerView(View v)
        +requestChildrenUpdate()
        -updateChildren()
        -startAnimationToState()
        -applyCurrentState()
        +onHeightChanged(ExpandableView view)
        +setOwnScrollY(int y)
    }

    class AmbientState {
        -int mScrollY
        -float mStackTranslation
        -boolean mDimmed
        -boolean mHideSensitive
        -float mOverScrollTopAmount
        -float mOverScrollBottomAmount
        -ExpandableView mActivatedChild
        -int mLayoutHeight
        -int mLayoutMinHeight
        -NotificationShelf mShelf

        +getScrollY() int
        +setScrollY(int y)
        +getStackTranslation() float
        +setStackTranslation(float translation)
        +isDimmed() boolean
        +setDimmed(boolean dimmed)
        +getLayoutHeight() int
    }

    class StackScrollAlgorithm {
        -StackScrollAlgorithmState mTempAlgorithmState
        -boolean mIsExpanded

        +resetViewStates(AmbientState ambientState)
        +getStackScrollState(AmbientState ambientState, StackScrollAlgorithmState algorithmState)
        -updatePositionsForState(StackScrollAlgorithmState algorithmState, AmbientState ambientState)
        -clampPositionToShelf(ExpandableViewState childViewState, NotificationShelf shelf)
        +getGapHeightForChild(...) float
        -computeCornerRoundnessForPinnedHun(...)
    }

    class StackStateAnimator {
        -NotificationStackScrollLayout mHostLayout
        -ArrayList~AnimationProperties~ mAnimationProperties
        -AnimationFilter mAnimationFilter
        -boolean mAnimationRunning

        +startAnimationForEvents(ArrayList~AnimationEvent~ events, long delay)
        -processAnimationEvents(ArrayList~AnimationEvent~ events)
        -startStackAnimations(...)
        -startViewAnimations(...)
        +animateOverScrollToAmount(float amount, boolean onTop)
        -onAnimationFinished()
    }

    class ExpandableNotificationRow {
        -ExpandableViewState mViewState
        -NotificationEntry mEntry
        -boolean mUserExpanded
        -int mActualHeight

        +getViewState() ExpandableViewState
        +applyViewState()
        +setUserExpanded(boolean expanded)
        +getActualHeight() int
        +setActualHeight(int height)
    }

    class ExpandableViewState {
        +float yTranslation
        +float zTranslation
        +float alpha
        +int height
        +boolean dimmed
        +boolean hideSensitive
        +boolean inShelf
        +float scale

        +applyToView(View view)
        +animateTo(View child, AnimationProperties properties)
        +copyFrom(ExpandableViewState viewState)
    }

    class AnimationEvent {
        +ExpandableView mChangingView
        +int animationType
        +AnimationFilter filter
        +long length
        +View viewAfterChangingView

        +combineLength(ArrayList~AnimationEvent~ events) long
    }

    class AnimationFilter {
        +boolean animateAlpha
        +boolean animateY
        +boolean animateZ
        +boolean animateHeight
        +boolean animateDimmed
        +boolean hasDelays

        +combineFilter(AnimationFilter filter)
        +applyCombination(AnimationFilter filter)
    }

    %% 组合关系
    NotificationStackScrollLayout *-- AmbientState : 持有
    NotificationStackScrollLayout *-- StackScrollAlgorithm : 持有
    NotificationStackScrollLayout *-- StackStateAnimator : 持有
    NotificationStackScrollLayout o-- ExpandableNotificationRow : 管理多个

    %% 依赖关系
    StackScrollAlgorithm ..> AmbientState : 读取状态
    StackScrollAlgorithm ..> ExpandableViewState : 计算并更新
    StackStateAnimator ..> AnimationEvent : 处理
    StackStateAnimator ..> ExpandableViewState : 动画到目标状态
    StackStateAnimator ..> AnimationFilter : 使用

    %% 关联关系
    ExpandableNotificationRow --> ExpandableViewState : 持有
    AnimationEvent --> ExpandableNotificationRow : 引用
    AnimationEvent --> AnimationFilter : 持有

    %% 继承关系
    ExpandableNotificationRow --|> ExpandableView : 继承

0x04. 流程图

展示从用户操作到最终渲染的完整数据流

flowchart TD
    Start([用户操作
滚动/添加/展开通知]) --> Request[NSSL.requestChildrenUpdate] Request --> CheckFlag{mChildrenUpdateRequested?} CheckFlag -->|false| Register[注册 OnPreDrawListener] CheckFlag -->|true| End1([已注册,等待执行]) Register --> SetFlag[mChildrenUpdateRequested = true] SetFlag --> Invalidate[invalidate 触发重绘] Invalidate --> PreDraw[下一帧: OnPreDraw] PreDraw --> UpdateChildren[updateChildren] UpdateChildren --> UpdateScroll[updateScrollStateForAddedChildren
处理新添加View的滚动] UpdateScroll --> SetAmbient[更新 AmbientState
- setScrollY
- setCurrentScrollVelocity
- setAnchorViewIndex等] SetAmbient --> CallAlgo[调用 StackScrollAlgorithm
resetViewStates] CallAlgo --> AlgoRead[算法读取 AmbientState
- getScrollY
- getStackTranslation
- getLayoutHeight等] AlgoRead --> AlgoCalc[遍历子View计算
- yTranslation位置
- alpha透明度
- height高度
- zTranslation深度
- dimmed/scale等] AlgoCalc --> AlgoWrite[更新各子View的
ExpandableViewState] AlgoWrite --> CheckAnim{需要动画
mNeedsAnimation} CheckAnim -->|true| GenEvents[生成动画事件
generateAllAnimationEvents] CheckAnim -->|false| ApplyDirect[直接应用状态
applyCurrentState] GenEvents --> StartAnim[StackStateAnimator
startAnimationForEvents] StartAnim --> ProcessEvents[处理动画事件
- ANIMATION_TYPE_ADD
- ANIMATION_TYPE_REMOVE
- ANIMATION_TYPE_CHANGE_POSITION等] ProcessEvents --> CreateAnim[创建属性动画
ObjectAnimator] CreateAnim --> RunAnim[执行动画
改变View属性] RunAnim --> AnimEnd[动画结束回调
onAnimationFinished] ApplyDirect --> Loop1[遍历所有子View] Loop1 --> ApplyState[调用 child.applyViewState
直接设置属性] AnimEnd --> UpdateBg[updateBackground
更新背景] ApplyState --> UpdateBg UpdateBg --> UpdateShadow[updateViewShadows
更新阴影] UpdateShadow --> UpdateClip[updateClippingToTopRoundedCorner
更新圆角裁剪] UpdateClip --> ClearFlag[mChildrenUpdateRequested = false
mNeedsAnimation = false] ClearFlag --> End2([更新完成]) style Start fill:#e1f5ff style AmbientState fill:#fff4e1 style CallAlgo fill:#e8f5e8 style StartAnim fill:#ffe8f5 style End2 fill:#e1ffe1

《夸克》非常好用的免费AI浏览器

下载APP查看

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多