【奶茶Beta专项】【LVGL9.4源码分析】03-显示框架-图层管理

【奶茶Beta专项】【LVGL9.4源码分析】03-显示框架-图层管理

  • [1. 概述](#1. 概述)
    • [1.1 文档目的](#1.1 文档目的)
    • [1.2 代码版本与范围](#1.2 代码版本与范围)
  • [2. 字段总览与 LVGL 8.4.0 对比](#2. 字段总览与 LVGL 8.4.0 对比)
    • [2.1 字段含义](#2.1 字段含义)
      • [2.1.1 Layer 相关字段](#2.1.1 Layer 相关字段)
      • [2.1.2 Screens 与基础图层字段](#2.1.2 Screens 与基础图层字段)
      • [2.1.3 屏幕切换相关字段](#2.1.3 屏幕切换相关字段)
    • [2.2 与 LVGL 8.4.0 的差异对比](#2.2 与 LVGL 8.4.0 的差异对比)
      • [2.2.1 绘制管线:从"直接刷新"到"多 Layer 合成"](#2.2.1 绘制管线:从“直接刷新”到“多 Layer 合成”)
      • [2.2.2 屏幕存储与计数方式](#2.2.2 屏幕存储与计数方式)
      • [2.2.3 Layer 与基础图层的职责划分](#2.2.3 Layer 与基础图层的职责划分)
  • [3. 图层叠加关系(Layer Stack)](#3. 图层叠加关系(Layer Stack))
    • [3.1 静态叠加顺序概览](#3.1 静态叠加顺序概览)
    • [3.2 `draw_prev_over_act` 对叠加顺序的影响](#3.2 draw_prev_over_act 对叠加顺序的影响)
      • [3.2.1 非 "out" 型动画(例如新屏滑入)](#3.2.1 非 “out” 型动画(例如新屏滑入))
      • [3.2.2 "out" 型动画(旧屏滑出 / 渐隐)](#3.2.2 “out” 型动画(旧屏滑出 / 渐隐))
  • [4. 屏幕切换场景与字段协作](#4. 屏幕切换场景与字段协作)
    • [4.1 无动画加载(`lv_screen_load` 或 time=0)](#4.1 无动画加载(lv_screen_load 或 time=0))
    • [4.2 带动画加载(`lv_screen_load_anim`)](#4.2 带动画加载(lv_screen_load_anim))
      • [4.2.1 状态机关键字段](#4.2.1 状态机关键字段)
      • [4.2.2 典型动画 case 列举](#4.2.2 典型动画 case 列举)
  • [5. Layer 与屏幕生命周期流程](#5. Layer 与屏幕生命周期流程)
    • [5.1 Display 创建阶段](#5.1 Display 创建阶段)
    • [5.2 屏幕切换与字段状态演变](#5.2 屏幕切换与字段状态演变)
    • [5.3 Layer 链表的创建与销毁](#5.3 Layer 链表的创建与销毁)
  • [6. 实战建议与常见用法](#6. 实战建议与常见用法)
    • [6.1 如何合理使用 bottom / act / top / sys 四层](#6.1 如何合理使用 bottom / act / top / sys 四层)
    • [6.2 关于 `del_prev` 的取舍](#6.2 关于 del_prev 的取舍)
    • [6.3 使用 `layer_init` / `layer_deinit` 做后端适配](#6.3 使用 layer_init / layer_deinit 做后端适配)

文档版本 : 1.0
更新日期 : 2025年11月
适用对象: LVGL9.4 显示驱动 / UI 架构 / 应用开发工程师

1. 概述

1.1 文档目的

本篇聚焦 LVGL9.4 显示框架中的图层与屏幕管理模型 :围绕 struct _lv_display_t 中的 layer_headlayer_initlayer_deinitscreenssys_layertop_layeract_scrbottom_layerprev_scrscr_to_loadscreen_cntdraw_prev_over_actdel_prev 等字段,说明它们的含义、彼此关系,以及在刷新与屏幕切换流程中的作用。

目标是帮助读者:

  • 理解 LVGL9.4 中"绘制图层(lv_layer_t 链表)+ 屏幕对象(lv_obj_t)"的整体结构;
  • 搞清楚 bottom_layer / act_scr / prev_scr / top_layer / sys_layer 的叠加顺序;
  • 掌握 屏幕切换时这些字段如何协同工作,如何通过动画与标志位控制旧屏的保留与销毁;
  • 为后续进行复杂 UI 架构设计(多屏、多覆盖层、全局浮层等)提供可直接参考的实现依据。

1.2 代码版本与范围

  • 仓库路径:https://github.com/lvgl/lvgl.git
  • 版本:v9.4.0
  • commit: c016f72d4c125098287be5e83c0f1abed4706ee5
  • 重点文件:
    • src/display/lv_display_private.hstruct _lv_display_t 定义)
    • src/display/lv_display.c(display 创建、图层与屏幕管理)
    • src/core/lv_refr.c(刷新与图层叠加顺序)
    • src/draw/lv_draw.clv_layer_t 链表的创建与销毁)

2. 字段总览与 LVGL 8.4.0 对比

2.1 字段含义

2.1.1 Layer 相关字段

struct _lv_display_t 中,Layer 小节如下(省略无关成员):

text 复制代码
/*---------------------
 * Layer
 *--------------------*/
lv_layer_t * layer_head;
void (*layer_init)(lv_display_t * disp, lv_layer_t * layer);
void (*layer_deinit)(lv_display_t * disp, lv_layer_t * layer);
  • lv_layer_t * layer_head

    • 含义:当前 display 的绘制图层链表头结点
    • 作用:
      • 刷新时从 layer_head 开始遍历每一个 lv_layer_t,为其分配 / 派发 draw task,并最终把各 layer 的离屏缓冲合成到最终显示缓冲;
      • 默认情况下,layer_head 对应一块覆盖整屏的"主 Layer",后续可以派生子 Layer(例如截图、渐变遮罩、GPU 中间层等);
    • 生命周期:
      • lv_display_create() 中通过 lv_malloc(sizeof(lv_layer_t)) 创建,并调用 lv_layer_init() 初始化;
      • lv_display_delete() 中遍历并释放,最终释放 layer_head 本身。
  • void (*layer_init)(lv_display_t * disp, lv_layer_t * layer)

    • 含义:可选的 Layer 初始化回调;
    • 调用时机:
      • display 创建时,对 disp->layer_head 调用一次;
      • 后续通过 lv_draw_layer_init() 创建新的 lv_layer_t 时,如果 disp->layer_init 非空,也会对新 layer 调用一次;
    • 典型用途:
      • 为每个 Layer 绑定底层 GPU / 渲染后端相关的资源(例如 OpenGL 纹理、SDL 纹理、专用缓冲池等);
      • 设置 Layer 级别的默认参数(如混合模式、颜色格式、透明度策略等)。
  • void (*layer_deinit)(lv_display_t * disp, lv_layer_t * layer)

    • 含义:可选的 Layer 反初始化回调;
    • 调用时机:
      • 当某个 lv_layer_t 即将被销毁(从 layer_head 链表摘除并 lv_free() 之前),如果设置了回调则先执行 layer_deinit()
    • 典型用途:
      • 释放在 layer_init() 中分配的 GPU / 后端相关资源;
      • 做调试统计、日志输出(例如 Layer 生命周期监控)。

2.1.2 Screens 与基础图层字段

text 复制代码
/*---------------------
 * Screens
 *--------------------*/

/** Screens of the display*/
lv_obj_t ** screens;    /**< Array of screen objects.*/
lv_obj_t * sys_layer;   /**< @see lv_display_get_layer_sys*/
lv_obj_t * top_layer;   /**< @see lv_display_get_layer_top*/
lv_obj_t * act_scr;     /**< Currently active screen on this display*/
lv_obj_t * bottom_layer;/**< @see lv_display_get_layer_bottom*/
lv_obj_t * prev_scr;    /**< Previous screen. Used during screen animations*/
lv_obj_t * scr_to_load; /**< The screen prepared to load in lv_screen_load_anim*/
uint32_t screen_cnt;
uint8_t draw_prev_over_act  : 1;/** 1: Draw previous screen over active screen*/
uint8_t del_prev  : 1;  /** 1: Automatically delete the previous screen when the screen load animation is ready*/
  • lv_obj_t ** screens

    • 含义:当前 display 上所有"屏幕对象"(root screen)的指针数组;
    • 特点:
      • 每一个 screen 本质上是一个 lv_obj_t *,但 parent 为 NULL,代表一个独立的坐标空间根节点;
      • screens[i] 中的对象与 screen_cnt 一一对应,screen_cnt 记录该数组的有效长度;
    • 典型用途:通过 lv_display_get_screen_by_name() 或其他 API 在所有 screen 中查找、枚举。
  • lv_obj_t * act_scr

    • 含义:当前**激活(正在显示)**的 screen;
    • 刷新时机:
      • 在刷新函数中,会从 act_scr 开始,递归绘制该 screen 及其子树,叠加到底层 bottom_layer 以及上层 top_layer / sys_layer 上;
    • 生命周期:
      • display 创建时默认创建一个 act_scrlv_obj_create(NULL));
      • 通过 lv_screen_load() / lv_screen_load_anim() 切换时,会修改 act_scr 指针。
  • lv_obj_t * sys_layer

    • 含义:系统级覆盖层(system layer),永远在所有普通 UI 之上;
    • 典型用途:
      • 系统提示、监控信息(如 FPS overlay、调试信息、光标等);
      • 与输入设备、系统服务强相关的全局对象;
    • 特点:在刷新时总是最后绘制(见 lv_refr.c 中的调用顺序)。
  • lv_obj_t * top_layer

    • 含义:用户级"顶层覆盖层"(top layer),位于普通 screen 之上,但在 sys_layer 之下;
    • 典型用途:
      • 弹框、遮罩层、全局菜单、拖拽预览等;
      • 可以承载需要"压住当前 screen,但不属于任何一个具体 screen"的 UI。
  • lv_obj_t * bottom_layer

    • 含义:底层背景 layer,位于所有 screen 下方;
    • 典型用途:
      • 显示统一背景(如壁纸、统一底纹);
      • 在没有任何 screen 内容时,作为兜底绘制对象;
    • 特点:display 创建时自动创建,通常去掉所有样式,只在需要时由用户设置内容。

2.1.3 屏幕切换相关字段

  • lv_obj_t * prev_scr

    • 含义:上一个 screen,用于屏幕切换动画过程;
    • 行为:
      • lv_screen_load_anim() 启动时,prev_scr 会被设置为旧的 act_scr
      • 部分动画类型中,prev_scr 与新 act_scr 会根据 draw_prev_over_act 的值叠加绘制;
      • 动画完成后在 scr_anim_completed() 中被清空,必要时根据 del_prev 删除。
  • lv_obj_t * scr_to_load

    • 含义:预备加载的目标 screen
    • 典型时序:
      • 调用 lv_screen_load_anim(new_scr, ...) 后,scr_to_load 先被设置为 new_scr
      • 如果已经有一个动画在进行,且存在 scr_to_load,会先把之前的 scr_to_load 提前"落地"为当前 act_scr,再切到新的 new_scr
      • scr_load_internal() 或动画完成时被清空。
  • uint32_t screen_cnt

    • 含义:screens 数组中有效 screen 的数量;
    • 更新时机:创建 / 删除 screen 时维护,用于遍历与统计。
  • uint8_t draw_prev_over_act : 1

    • 含义:屏幕切换动画时,是否需要把上一个 screen 叠加在当前 screen 之上绘制;
    • 典型用途:
      • 对于"旧屏幕滑出 / 渐隐"的 out 动画,需要先绘制当前新屏,再把旧屏以动画形式覆盖在上面;
      • 对于"新屏幕从侧边滑入"的 in 动画,则通常先绘制旧屏,再在其上绘制新屏;
    • 设置位置:在 lv_screen_load_anim() 中通过 is_out_anim(anim_type) 计算得到。
  • uint8_t del_prev : 1

    • 含义:屏幕切换动画完成后,是否自动删除上一个 screen
    • 行为:
      • auto_del == true 传入 lv_screen_load_anim() 时,该标志置为 1;
      • scr_anim_completed() 中,如果 prev_scr 非空且 del_prev == 1,则调用 lv_obj_delete(prev_scr) 并清空指针;
    • 典型用途:防止长期积累"已经不用的 screen"导致内存泄露。

2.2 与 LVGL 8.4.0 的差异对比

2.2.1 绘制管线:从"直接刷新"到"多 Layer 合成"

  • 8.4.0

    • 主要通过 lv_disp_t + lv_disp_drv_t + lv_disp_draw_buf_t 组合完成刷新;
    • 没有统一的 lv_layer_t 抽象,绘制管线更多地围绕"区域重绘(invalid area)+ 直接写入 draw buffer"展开;
    • 想要做多层离屏合成(如复杂特效、多级遮罩),往往需要用户自行维护中间缓冲与刷新的顺序。
  • 9.4.0

    • 引入 lv_layer_t 作为通用绘制图层,每个 Layer 可以绑定独立的缓冲、颜色格式和裁剪区域;
    • layer_head 指向所有 Layer 的链表头,刷新流程中会遍历所有 Layer 并按照依赖关系合成;
    • 通过 layer_init / layer_deinit 为 GPU / 后端适配预留钩子,使多后端、多平台的绘制管线更加统一。

总结:9.4 的图层系统让"画什么 + 在哪里合成"变得更加模块化,便于在 MCU、Linux、PC 模拟器等不同平台上共享同一套逻辑。

2.2.2 屏幕存储与计数方式

  • 8.4.0

    • 使用 scr_ll 等链表结构保存 screen,更多从"链表遍历"角度组织;
    • 没有显式的 screen_cnt 字段,统计数量通常需要遍历;
    • 屏幕检索能力相对简单。
  • 9.4.0

    • 使用 lv_obj_t ** screens 数组配合 screen_cnt 显式记录所有 screen;
    • 提供按名称查找 screen 等更高层的 API;
    • 为后续做统计、调试(例如打印所有 screen 列表)提供了更清晰的数据结构。

2.2.3 Layer 与基础图层的职责划分

  • 8.4.0

    • 已经存在 top_layer / sys_layer 等概念,但更多侧重"额外 UI 层",与 draw pipeline 的关系较弱;
    • 没有独立的 bottom_layer,底层背景通常直接由 act_scr 或某个根对象承担。
  • 9.4.0

    • 把绘制相关的"Layer"(lv_layer_t)与 UI 语义上的"Layer"(bottom_layer / top_layer / sys_layer / act_scr / prev_scr)清晰区分:
      • lv_layer_t 负责像素级缓冲与合成
      • 各种 lv_obj_t * 字段负责对象层级与叠加顺序
    • 新增 bottom_layer,让 UI 背景与普通 screen 分离,增强了整体布局的灵活性。

3. 图层叠加关系(Layer Stack)

3.1 静态叠加顺序概览

在不考虑动画和透明度的前提下,从屏幕底部到顶部的大致叠加顺序可以用 ASCII 图示意如下(原点在左上角,X 向右、Y 向下):

text 复制代码
Y
^
|   (靠上的是"更靠前"的内容,即后绘制 → 覆盖前绘制)
|
|   +---------------------------------------------+   ← sys_layer   (系统级浮层,最前)
|   |                 System Layer               |
|   +---------------------------------------------+
|   +---------------------------------------------+   ← top_layer   (全局弹框 / 遮罩)
|   |                   Top Layer                |
|   +---------------------------------------------+
|   +---------------------------------------------+   ← act_scr / prev_scr (当前屏 / 上一屏)
|   |                Active Screen                |
|   |              (and maybe Prev Screen)       |
|   +---------------------------------------------+
|   +---------------------------------------------+   ← bottom_layer (统一背景)
|   |                Bottom Layer                 |
|   +---------------------------------------------+
+------------------------------------------------------> X
             (物理显示面板 / 最终输出)

刷新逻辑(lv_refr.c)中,大致按照:

  1. 先决定是否需要绘制 bottom_layer(例如没有可见 top 对象时);
  2. 再根据 draw_prev_over_act 的值决定 prev_scract_scr 的绘制顺序;
  3. 最后始终无条件绘制 top_layersys_layer

3.2 draw_prev_over_act 对叠加顺序的影响

可以分两类典型情况来看:

3.2.1 非 "out" 型动画(例如新屏滑入)

此时 draw_prev_over_act == 0,绘制顺序(忽略透明度)大致如下:

text 复制代码
1) Bottom Layer
2) Prev Screen (若存在)
3) Active Screen (New)
4) Top Layer
5) Sys Layer

直观理解:旧屏先画,新屏覆盖在上面,再加各种浮层。

3.2.2 "out" 型动画(旧屏滑出 / 渐隐)

对于 LV_SCREEN_LOAD_ANIM_OUT_* / LV_SCREEN_LOAD_ANIM_FADE_OUT 等动画,draw_prev_over_act == 1,绘制顺序变为:

text 复制代码
1) Bottom Layer
2) Active Screen (New)
3) Prev Screen  (Old, 在上面做 out 动画)
4) Top Layer
5) Sys Layer

此时视觉效果变为:

先看到新屏幕内容,然后旧屏从上方"滑走 / 渐隐",强调"旧屏离场"的感觉。

4. 屏幕切换场景与字段协作

本节结合 lv_display.c 中的实现,按典型 case 拆解 act_scrprev_scrscr_to_loaddraw_prev_over_actdel_prev 的协作方式。

4.1 无动画加载(lv_screen_load 或 time=0)

调用:

c 复制代码
lv_screen_load(new_scr);
// 等价于 lv_screen_load_anim(new_scr, LV_SCREEN_LOAD_ANIM_NONE, 0, 0, false);

关键路径:

  • 进入 lv_screen_load_anim() 后:
    • d->scr_to_load = new_scr;
    • 检查 time == 0 && delay == 0,直接走"立即加载"分支;
  • 调用 scr_load_internal(new_scr)
    • prev_scr 不参与动画,只用于事件通知;
    • d->act_scr = new_scr;
    • d->scr_to_load = NULL;
  • 不触发 draw_prev_over_act 相关逻辑,也不会自动删除旧屏,是否删除由上层自行决定。

实战解读:

  • 适合"直接切换界面"的场景,如启动阶段、无须过渡动画的配置页面切换;
  • 若要"顺带销毁旧屏",可以在调用前后主动 lv_obj_delete(old_scr),而不是依赖 del_prev

4.2 带动画加载(lv_screen_load_anim

4.2.1 状态机关键字段

lv_screen_load_anim() 中可概括为:

text 复制代码
d->scr_to_load      = new_scr;
if (d->prev_scr && d->del_prev) delete(d->prev_scr);
d->prev_scr         = NULL;
d->draw_prev_over_act = is_out_anim(anim_type);
d->del_prev         = auto_del;

之后:

  • scr_load_anim_start():在动画开始时,将
    • d->prev_scr = d->act_scr;
    • d->act_scr = new_scr;
  • scr_anim_completed():在动画完成时,根据
    • if (d->prev_scr && d->del_prev) delete(d->prev_scr);
    • 然后清空 prev_scr / draw_prev_over_act / scr_to_load

4.2.2 典型动画 case 列举

结合 anim_type 可以归纳几类典型行为(省略时间和延迟):

  • Case A:无动画但延迟执行(LV_SCREEN_LOAD_ANIM_NONE + time > 0

    • draw_prev_over_act = 0
    • 在延迟期不渲染前后屏的过渡,只是等待时间到达后立即切换;
    • 适合"定时切屏但不需要动画"的场景。
  • Case B:新屏滑入 / 渐显(典型 in 动画)

    • LV_SCREEN_LOAD_ANIM_MOVE_LEFTMOVE_RIGHTMOVE_TOPMOVE_BOTTOMFADE_IN 等;
    • 通常被视为"in" 型动画,对应 draw_prev_over_act = 0
    • 绘制顺序:旧屏在下,新屏在上做进入动画,动画完成后旧屏按 del_prev 决定是否删除。
  • Case C:旧屏滑出 / 渐隐(典型 out 动画)

    • LV_SCREEN_LOAD_ANIM_OUT_LEFTOUT_RIGHTOUT_TOPOUT_BOTTOMFADE_OUT 等;
    • draw_prev_over_act = 1
    • 绘制顺序:新屏在下,旧屏在上做离场动画,动画完成后旧屏按 del_prev 决定是否删除。

对于每一种动画类型,重点关注:

  • draw_prev_over_act:决定谁在上面、谁在下面;
  • del_prev:决定旧屏是否在动画结束后自动释放;
  • scr_to_load:在多次连续调用 lv_screen_load_anim() 时,避免"动画堆积",同时确保最后一个目标 screen 能落地。

5. Layer 与屏幕生命周期流程

5.1 Display 创建阶段

lv_display_create() 中,关键步骤可以简化为:

text 复制代码
1) 创建 lv_display_t 并初始化分辨率、颜色格式等基础字段
2) 分配并初始化 layer_head
   - lv_layer_init(disp->layer_head)
   - 若设置了 layer_init,则调用 layer_init(disp, layer_head)
   - 设置 layer_head.buf_area = 全屏区域
3) 创建底层 / 顶层 / 系统层对象:
   - bottom_layer = lv_obj_create(NULL)
   - act_scr      = lv_obj_create(NULL)
   - top_layer    = lv_obj_create(NULL)
   - sys_layer    = lv_obj_create(NULL)
   - 移除多余样式 / 标志,确保作为纯容器使用
4) 根据主题 / 样式配置对 act_scr 等做后续初始化

要点:

  • layer_head 管"像素级缓冲与合成",bottom_layer / act_scr / top_layer / sys_layer 管"对象层级与 UI 语义";
  • layer_init / layer_deinit 只在 创建 / 销毁 lv_layer_t 时被调用,不负责 screen 对象的生命周期。

5.2 屏幕切换与字段状态演变

下面以带动画的典型场景为例,用简化的 ASCII 流程串起相关字段:

text 复制代码
[初始状态]
  act_scr = S0
  prev_scr = NULL
  scr_to_load = NULL
  draw_prev_over_act = 0
  del_prev = 0

[调用 lv_screen_load_anim(S1, anim_type, time, delay, auto_del)]
  ├─ scr_to_load = S1
  ├─ 若存在旧的 prev_scr 且 del_prev=1,则 delete(prev_scr),prev_scr=NULL
  ├─ draw_prev_over_act = is_out_anim(anim_type)
  └─ del_prev = auto_del

[动画开始 scr_load_anim_start]
  ├─ prev_scr = act_scr (= S0)
  └─ act_scr  = S1

[刷新阶段 lv_refr.c]
  ├─ 根据 draw_prev_over_act 选择先画谁、后画谁
  └─ 始终叠加 top_layer / sys_layer

[动画完成 scr_anim_completed]
  ├─ 发送 LOADED / UNLOADED 事件
  ├─ 若 prev_scr 非空且 del_prev=1,则 delete(prev_scr)
  ├─ prev_scr = NULL
  ├─ draw_prev_over_act = 0
  └─ scr_to_load = NULL

通过上面的状态演变,可以清晰看到:

  • act_scr:始终代表"当前已经激活的 screen";
  • scr_to_load:代表"正在规划中但尚未真正生效的目标 screen";
  • prev_scr:仅在动画期有效,结束后按需删除或保留;
  • draw_prev_over_act / del_prev:控制视觉叠加顺序内存回收策略

5.3 Layer 链表的创建与销毁

在复杂场景中,除了默认的 layer_head 外,还会通过 lv_draw_layer_create() 创建更多中间 Layer(例如截图、离屏特效等)。生命周期简要如下:

text 复制代码
[创建]
  layer = lv_draw_layer_create(parent_layer, color_format, area)
    ├─ 分配 lv_layer_t
    ├─ lv_draw_layer_init(layer, parent_layer, color_format, area)
    │   ├─ lv_layer_init(layer)
    │   ├─ 设置裁剪区域 / 颜色格式等
    │   ├─ 若 disp->layer_init 非空,调用 layer_init(disp, layer)
    │   └─ 挂入 disp->layer_head 链表尾部
    └─ 返回 layer

[销毁(draw task 完成后)]
  ├─ 从 disp->layer_head 链表中摘除该 layer
  ├─ 若 disp->layer_deinit 非空,调用 layer_deinit(disp, layer)
  └─ lv_free(layer)

这套机制保证了:
绘制图层的生命周期由刷新与绘制模块自动管理,而 display 层只需通过回调感知 Layer 的创建与销毁,以便做后端资源管理。

6. 实战建议与常见用法

6.1 如何合理使用 bottom / act / top / sys 四层

  • bottom_layer

    • 建议只放全局性背景(纯色、图案、壁纸),不要放与业务强耦合的控件;
    • 优点是:切换 screen 时背景保持稳定,视觉上更连贯。
  • act_scr

    • 每个业务界面一个 screen,内部再划分多个容器;
    • 通过 lv_screen_load() / lv_screen_load_anim() 切换不同 screen。
  • top_layer

    • 放置需要覆盖当前 screen 的业务 UI,例如弹窗、对话框、提示条等;
    • 避免在多个 screen 中各自维护一套重复的弹层结构。
  • sys_layer

    • 更适合与系统 / 输入设备强绑定的 UI(如鼠标指针、调试信息、FPS 统计、全局遮罩等);
    • 一般由框架或上层平台统一管理,业务模块通常不直接操作。

6.2 关于 del_prev 的取舍

  • 适合开启自动删除的情况

    • 大多数"页面式 UI",旧 screen 在新 screen 生效后已经完全不需要;
    • 界面树比较大,频繁切换时如果不删除会快速占满内存。
  • 适合关闭自动删除的情况

    • 需要在若干 screen 之间频繁往返切换,并希望保留其内部状态(例如表单内容、滚动位置等);
    • 由业务逻辑统一管理"哪些 screen 长期存在、哪些可以被销毁",不希望动画逻辑擅自删除。

建议做法:

  • 对于"菜单 → 详情页"这类一次性跳转,可开启 auto_del 简化内存管理;
  • 对于"多 Tab 页之间来回切换"这类场景,建议关闭 auto_del,并在业务结束时手工清理不再需要的 screen。

6.3 使用 layer_init / layer_deinit 做后端适配

如果项目中接入了 GPU / 特定渲染后端,可以考虑:

  • layer_init 中:

    • 为每个 lv_layer_t 分配对应的 GPU 纹理 / Framebuffer;
    • 记录与后端相关的句柄到 layer->user_data 或 display 自定义结构中。
  • layer_deinit 中:

    • 释放这些 GPU 资源,写入必要的调试日志;
    • 做 Layer 级别的统计(例如累计渲染次数、峰值大小)。

这样可以利用 LVGL9.4 的 Layer 管理能力,将"图层生命周期"和"后端资源生命周期"绑定起来,从而实现更清晰的渲染架构。


通过本篇,读者应当已经能够从源码角度理解:
LVGL9.4 中 display 层的"图层管理"并不是单一字段的简单堆叠,而是一套围绕 lv_layer_t、基础层对象和屏幕切换状态字段精心设计的系统。

在实际工程中,只要搞清楚这些字段的含义与协作方式,就可以比较自信地实现多屏、多浮层、复杂动画的 UI 设计。

相关推荐
last demo1 小时前
LNMP部署实验
linux·运维·服务器
云雾J视界1 小时前
51单片机信号处理实战:C语言A/D与D/A转换应用,从传感器采集到PWM控制全解析
c语言·51单片机·信号处理·pwm·模拟信号·数字信号·a/d
WongKyunban1 小时前
使用Valgrind检测内存问题(C语言)
c语言·开发语言
代码游侠1 小时前
数据结构——线性表
linux·c语言·数据结构·学习·算法
蒋士峰DBA修行之路1 小时前
红帽练习环境介绍
linux·开发语言·bash
。TAT。1 小时前
进程间通信-对匿名管道的学习
linux·学习
2301_807583231 小时前
Linux-虚拟化技术概述及KVM虚拟机环境部署
linux·运维·服务器
HalvmånEver1 小时前
Linux:命令行参数与环境变量(进程五)
linux·运维·服务器
python百炼成钢1 小时前
43.Linux LCD驱动
java·linux·运维·驱动开发