【奶茶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 列举)
- [4.1 无动画加载(`lv_screen_load` 或 time=0)](#4.1 无动画加载(
- [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_head、layer_init、layer_deinit、screens、sys_layer、top_layer、act_scr、bottom_layer、prev_scr、scr_to_load、screen_cnt、draw_prev_over_act、del_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.h(struct _lv_display_t定义)src/display/lv_display.c(display 创建、图层与屏幕管理)src/core/lv_refr.c(刷新与图层叠加顺序)src/draw/lv_draw.c(lv_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 调用一次;
- display 创建时,对
- 典型用途:
- 为每个 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记录该数组的有效长度;
- 每一个 screen 本质上是一个
- 典型用途:通过
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_scr(lv_obj_create(NULL)); - 通过
lv_screen_load()/lv_screen_load_anim()切换时,会修改act_scr指针。
- display 创建时默认创建一个
-
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。
- 含义:用户级"顶层覆盖层"(top layer),位于普通 screen 之上,但在
-
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 分离,增强了整体布局的灵活性。
- 把绘制相关的"Layer"(
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)中,大致按照:
- 先决定是否需要绘制
bottom_layer(例如没有可见 top 对象时); - 再根据
draw_prev_over_act的值决定prev_scr与act_scr的绘制顺序; - 最后始终无条件绘制
top_layer与sys_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_scr、prev_scr、scr_to_load、draw_prev_over_act、del_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_LEFT、MOVE_RIGHT、MOVE_TOP、MOVE_BOTTOM、FADE_IN等; - 通常被视为"in" 型动画,对应
draw_prev_over_act = 0; - 绘制顺序:旧屏在下,新屏在上做进入动画,动画完成后旧屏按
del_prev决定是否删除。
- 如
-
Case C:旧屏滑出 / 渐隐(典型 out 动画)
- 如
LV_SCREEN_LOAD_ANIM_OUT_LEFT、OUT_RIGHT、OUT_TOP、OUT_BOTTOM、FADE_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 设计。