【奶茶Beta专项】【LVGL9.4源码分析】09-core-obj_class对象类系统
- [1 概述](#1 概述)
-
- [1.1 文档目的](#1.1 文档目的)
- [1.2 代码版本与范围](#1.2 代码版本与范围)
- [2 设计意图与总体定位](#2 设计意图与总体定位)
-
- [2.1 lv_obj_class 承担了什么角色](#2.1 lv_obj_class 承担了什么角色)
- [2.2 类描述结构的关键字段](#2.2 类描述结构的关键字段)
- [2.3 对象创建流程中的类系统参与](#2.3 对象创建流程中的类系统参与)
- [3 接口分类与 API 速查表](#3 接口分类与 API 速查表)
-
- [3.1 类相关核心接口](#3.1 类相关核心接口)
- [3.2 类行为元信息枚举](#3.2 类行为元信息枚举)
- [3.3 与属性系统的结合(在启用 LV_USE_OBJ_PROPERTY 时)](#3.3 与属性系统的结合(在启用 LV_USE_OBJ_PROPERTY 时))
- [4 设计优势与缺点(含案例)](#4 设计优势与缺点(含案例))
-
- [4.1 设计优势](#4.1 设计优势)
- [4.2 潜在缺点与注意事项](#4.2 潜在缺点与注意事项)
- [4.3 简单案例:自定义可编辑控件 + 默认 group 行为](#4.3 简单案例:自定义可编辑控件 + 默认 group 行为)
- [4.4 struct `_lv_obj_class_t` 在 9.4 与 8.4 的差异与演进](#4.4 struct
_lv_obj_class_t在 9.4 与 8.4 的差异与演进) -
- [4.4.1 结构体字段层面的主要变化](#4.4.1 结构体字段层面的主要变化)
- [4.4.2 接口与使用方式层面的差异与原因推测](#4.4.2 接口与使用方式层面的差异与原因推测)
- [5 与其它框架的对比与改进思路](#5 与其它框架的对比与改进思路)
-
- [5.1 与 AWTK 的对比](#5.1 与 AWTK 的对比)
- [5.2 与 Qt 的对比](#5.2 与 Qt 的对比)
- [5.3 与 Android / HTML/CSS 的对比](#5.3 与 Android / HTML/CSS 的对比)
- [5.4 可能的改进方向](#5.4 可能的改进方向)
- [6 小结](#6 小结)
- [7 附录](#7 附录)
-
- [A 参考文档(外部)](#A 参考文档(外部))
- [B 相关资源(CSDN 系列)](#B 相关资源(CSDN 系列))
文档版本 : 1.0
更新日期 : 2025年12月
适用对象: 希望深入理解 LVGL9.4 对象模型与类系统的工程师(框架设计 / 复杂控件开发 / 上层框架作者)
1 概述
1.1 文档目的
本篇聚焦 library/lvgl/src/core/lv_obj_class*,从框架开发者视角解析 LVGL9.4 对象类系统(lv_obj_class)的设计思路与在整体架构中的定位 ,说明它如何在纯 C 语言环境下实现类似 C++/Qt 的类与继承机制,并支撑 widgets、主题、属性系统等上层能力。
通过接口分类与案例分析,本篇希望帮助读者在阅读/扩展 LVGL 内核时做到"看得懂类系统、敢写自定义类、知道哪些地方能改/不能乱改",并为日后构建自己的 UI 框架或 DSL 提供参考。
1.2 代码版本与范围
- 仓库路径:
https://github.com/lvgl/lvgl.git - 版本:v9.4.0
- 主要关注源码文件:
library/lvgl/src/core/lv_obj_class.hlibrary/lvgl/src/core/lv_obj_class_private.hlibrary/lvgl/src/core/lv_obj_class.c
- 相关联模块:
lv_obj.c及其lv_obj_t定义;- widgets 目录下各
lv_xxx.c对象类的静态描述; - group、theme、property 等依赖类系统元信息的子系统。
2 设计意图与总体定位
2.1 lv_obj_class 承担了什么角色
LVGL 的所有 UI 对象(按钮、标签、滑条等)最终都基于 lv_obj_t,但不同类型的对象需要:
- 自己的构造/析构逻辑;
- 自己的事件处理与绘制逻辑;
- 自己的默认尺寸、可编辑性、是否默认加入 group 等元信息。
在 C 语言里没有内建的类与继承机制,LVGL 通过 lv_obj_class_t 实现了一个轻量级的"类系统":
- 用一个结构体描述"类"的所有元信息和回调函数;
- 支持基类/派生类关系(例如:
lv_obj_class→lv_btn_class→ 自定义按钮类); - 在对象创建与初始化时,根据类描述调用相应的构造、事件、绘制等回调。
可以把 lv_obj_class 大致类比为:
Qt 里的
QWidget派生体系 + 一部分元对象系统,但实现完全基于 C 结构体与函数指针。
2.2 类描述结构的关键字段
在 lv_obj_class_private.h 中,lv_obj_class_t(简化后)包含几类关键信息:
- 继承与回调 :
base_class:指向基类,用于构造/析构链条与属性继承;constructor_cb/destructor_cb:构造、析构函数指针;event_cb:类级事件处理回调;width_def/height_def:默认尺寸。
- 类行为元信息 :
editable:是否可编辑(配合lv_obj_is_editable());group_def:是否默认加入 group(配合lv_obj_is_group_def()与默认 group 机制);theme_inheritable:是否从基类继承主题。
- 属性系统相关(在启用 LV_USE_OBJ_PROPERTY 时) :
prop_index_start/prop_index_end、properties、properties_count:描述该类支持的属性 ID 范围及 getter/setter 表;- 可选的
property_names列表,用于将属性 ID 映射到字符串名。
这些字段共同决定了一个类在"创建对象、响应事件、继承样式/主题、参与 group/焦点导航、支持属性编辑"等方面的行为。
2.3 对象创建流程中的类系统参与
以典型的对象创建流程为例(简化版):
c
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
lv_obj_t * obj = lv_malloc(class_p->instance_size);
obj->class_p = class_p;
obj->parent = parent;
/* 省略若干基础字段初始化 */
return obj;
}
void lv_obj_class_init_obj(lv_obj_t * obj)
{
lv_obj_mark_layout_as_dirty(obj);
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj); /* 按当前主题为对象应用初始样式 */
lv_obj_construct(obj->class_p, obj); /* 递归调用类/基类的 constructor_cb */
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
/* 默认 group 处理 */
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
/* 通知父对象新增子节点 */
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj);
lv_obj_invalidate(obj);
}
}
其中有几个关键点:
- 对象的构造逻辑由
lv_obj_construct()按"先基类、后派生类"的顺序调用; - 可编辑性、默认 group 行为等通过
editable、group_def等字段参与后续逻辑; - 属性系统、主题系统、事件系统都以类描述为基础,附着在
lv_obj_t上运行。
3 接口分类与 API 速查表
说明:以下仅列出与类系统直接相关的核心接口,其他对象操作接口如
lv_obj_set_size等在对象系统文档中展开。
3.1 类相关核心接口
| 功能 | 接口 | 说明 |
|---|---|---|
| 创建指定类的对象 | lv_obj_class_create_obj(class_p, parent) |
根据类描述创建对象实例 |
| 初始化对象(构造) | lv_obj_class_init_obj(obj) |
调用类/基类构造函数、应用主题、处理默认 group 等 |
| 判断对象是否可编辑 | lv_obj_is_editable(obj) |
根据类链上的 editable 字段判断 |
| 判断对象是否默认进 group | lv_obj_is_group_def(obj) |
根据类链上的 group_def 字段判断 |
| 销毁对象(析构) | lv_obj_destruct(obj)(9.4) / _lv_obj_destruct(obj)(8.4,内部) |
触发析构链和资源释放,通常由框架内部或 lv_obj_del 等高层接口调用 |
3.2 类行为元信息枚举
| 枚举类型 | 取值 | 含义 |
|---|---|---|
lv_obj_class_editable_t |
LV_OBJ_CLASS_EDITABLE_INHERIT |
从基类继承可编辑性 |
LV_OBJ_CLASS_EDITABLE_TRUE |
当前类实例可编辑(如文本输入、滑条等) | |
LV_OBJ_CLASS_EDITABLE_FALSE |
当前类实例不可编辑 | |
lv_obj_class_group_def_t |
LV_OBJ_CLASS_GROUP_DEF_INHERIT |
从基类继承是否默认进 group |
LV_OBJ_CLASS_GROUP_DEF_TRUE |
创建时自动加入默认 group(若存在) | |
LV_OBJ_CLASS_GROUP_DEF_FALSE |
不自动加入默认 group | |
lv_obj_class_theme_inheritable_t |
LV_OBJ_CLASS_THEME_INHERITABLE_FALSE |
不从基类继承主题 |
LV_OBJ_CLASS_THEME_INHERITABLE_TRUE |
从基类继承主题 |
3.3 与属性系统的结合(在启用 LV_USE_OBJ_PROPERTY 时)
在启用对象属性系统时,lv_obj_class_t 会为每个类注册属性 ID 与对应的 getter/setter,实现类似"反射/统一属性访问"的能力。核心接口包括:
| 功能 | 接口/字段 | 说明 |
|---|---|---|
| 属性操作表 | properties[](在类实现文件中定义) |
每个元素包含属性 ID、setter、getter |
| 通用设置接口 | lv_obj_set_any(obj, prop_id, &value)(内部静态实现) |
根据属性 ID 调用对应 setter |
| 通用获取接口 | lv_obj_get_any(obj, prop_id, &value)(内部静态实现) |
根据属性 ID 调用对应 getter |
| 属性 ID 范围 | prop_index_start/prop_index_end |
标记该类属性在全局属性表中的切片范围 |
与 lv_obj_property.h 配合,这套机制可以被 UI 编辑器、脚本绑定、配置文件解析等工具使用,从而在不直接写 C 代码的情况下操控 LVGL 对象。
4 设计优势与缺点(含案例)
4.1 设计优势
- 在 C 环境下提供清晰的类与继承抽象 :
- 对象的构造、析构、事件处理、绘制逻辑都围绕
lv_obj_class_t组织; - 通过
base_class建立层次结构,支持"基础控件 + 复合控件 + 项目级自定义控件"的渐进扩展。
- 对象的构造、析构、事件处理、绘制逻辑都围绕
- 元信息与行为统一挂在类描述上,便于工具化 :
- 可编辑性(
editable)、默认 group 行为(group_def)、主题继承属性都集中在类里,让工具和上层框架可以读取这些信息; - 属性系统让 UI 编辑器/配置工具能够通过统一接口操控不同类的属性。
- 可编辑性(
- 与 group / theme / property 等子系统天然耦合,避免散乱判断 :
- "是否可编辑"、"是否默认进 group"这类决策不再散落在各个控件实现里,而是通过统一的类元信息由公共入口处理。
4.2 潜在缺点与注意事项
- 类描述写错的后果比较隐蔽 :
- 如果某个自定义类的
base_class设置错误,可能导致构造链/析构链异常,甚至内存布局出问题; editable/group_def/theme_inheritable配置不当,会在焦点导航、主题应用等环节表现为"莫名其妙"的行为。
- 如果某个自定义类的
- 类系统的间接层会增加理解门槛 :
- 初学者看 widgets 代码时,很容易迷失在"类描述 + 回调 + 构造链"里,不如直接函数调用直观;
- 调试时,需要同时看对象实例、类描述和基类,才能完全理解行为。
- 属性系统的引入增加了一定复杂度 :
- 对大部分只写 C 业务的工程师来说,属性系统可能一开始用不上,但类描述中仍然需要正确维护属性相关字段,以免工具或脚本绑定出现异常。
4.3 简单案例:自定义可编辑控件 + 默认 group 行为
假设你要实现一个项目级自定义控件 my_spinbox,期望的行为是:
- 它基于已有的
lv_spinbox能力; - 它是可编辑的;
- 在存在默认 group 时,新建的
my_spinbox自动加入默认 group,方便编码器导航。
类描述(示意)可能类似:
c
const lv_obj_class_t my_spinbox_class = {
.base_class = &lv_spinbox_class,
.constructor_cb = my_spinbox_constructor,
.destructor_cb = my_spinbox_destructor,
.event_cb = my_spinbox_event,
.instance_size = sizeof(my_spinbox_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.theme_inheritable = LV_OBJ_CLASS_THEME_INHERITABLE_TRUE,
.name = "my_spinbox",
};
创建对象时:
lv_obj_class_create_obj(&my_spinbox_class, parent)分配结构体并设置class_p;lv_obj_class_init_obj(obj)内部会:- 调用
lv_obj_construct(),先构造基类lv_spinbox,再构造my_spinbox; - 通过
lv_obj_is_editable()/lv_obj_is_group_def()等逻辑,让该控件在默认 group 下自动参与焦点导航。
- 调用
这展示了类系统在"行为扩展 + 焦点导航 + 主题继承"多个维度上的协同作用。
4.4 struct _lv_obj_class_t 在 9.4 与 8.4 的差异与演进
4.4.1 结构体字段层面的主要变化
从源码可以看到,8.4 与 9.4 中的 _lv_obj_class_t 在字段上有明显演进:
- 字段组织方式 :
- 8.4 中
_lv_obj_class_t直接在lv_obj_class.h中定义,对外完全可见; - 9.4 将完整定义移动到
lv_obj_class_private.h,对外头文件只暴露lv_obj_class_t类型和少量公共接口,强化了"类描述是内核私有实现细节"的约束。
- 8.4 中
- 属性系统相关字段 (9.4 新增):
- 新增
prop_index_start/prop_index_end、properties、properties_count等字段,用于描述该类在全局属性表中的切片范围和属性操作表; - 在启用
LV_USE_OBJ_PROPERTY_NAME时,还会有property_names/names_count,用于将属性 ID 映射为可读名字,方便编辑器/调试工具; - 8.4 中完全没有这些字段,属性系统能力相对有限。
- 新增
- 主题与名称相关字段 (9.4 新增):
- 新增
name字段,用于标记类名,便于调试与工具化输出; - 新增
theme_inheritable位字段,对应枚举lv_obj_class_theme_inheritable_t,明确控制"是否从基类继承主题"; - 8.4 里只通过样式和对象本身配置主题,没有在类层面明确挂出"主题继承策略"。
- 新增
- 尺寸与用户数据相关的调整 :
- 8.4 中
width_def/height_def使用lv_coord_t,9.4 中则改为int32_t,方便在属性系统/序列化中以整型处理默认尺寸; - 8.4 中
user_data受LV_USE_USER_DATA宏控制;9.4 的私有结构体中则直接内置user_data指针,更贴近"类描述的扩展挂点"这一角色。
- 8.4 中
- 行为位字段扩展 :
- 两个版本都包含
editable/group_def位字段,用来承载lv_obj_class_editable_t/lv_obj_class_group_def_t; - 9.4 在此基础上新增
theme_inheritable位,用于描述主题继承策略,使得"可编辑性 / 默认 group / 主题继承"三类行为元信息在类层面一次性声明清楚。
- 两个版本都包含
总体来看,9.4 的 _lv_obj_class_t 在"属性系统、主题系统、调试友好性"三个维度上都比 8.4 更完备,更加明确地把"类 = 行为 + 元数据 + 属性表"这一概念固化到了结构体本身。
4.4.2 接口与使用方式层面的差异与原因推测
结合 8.4 与 9.4 的 lv_obj_class.h 可以大致看到如下变化趋势:
- 对外接口的收敛与稳定 :
- 两个版本都保留了
lv_obj_class_create_obj()、lv_obj_class_init_obj()、lv_obj_is_editable()、lv_obj_is_group_def()等核心接口,保证上层控件代码的兼容性; - 8.4 对
_lv_obj_destruct()有公开声明,而 9.4 把实际销毁逻辑下沉为lv_obj_destruct()等更高层接口,配合私有头文件,将"对象生命周期管理"的细节进一步封装在内核中。
- 两个版本都保留了
- 属性/主题系统驱动的设计强化 :
- 9.4 在类描述里增加属性表和主题继承控制,使"通过类元信息驱动工具与上层框架"的能力显著增强;
- 从演进轨迹看,9.4 明确是朝着"更适合 UI 编辑器、配置驱动、脚本绑定"的方向前进,而不仅仅是一个 C 级别的对象抽象层。
- 演进原因的推测 :
- 随着 LVGL 在"可视化编辑器 / 配置平台 / 高层脚本框架"中的使用增多,仅靠 8.4 版本那种轻量级类描述已经不够表达所有需要的元信息;
- 通过把
_lv_obj_class_t强化为"类的统一描述中心",9.4 能够:- 在一个地方集中管理属性、主题、可编辑性、默认 group 等行为;
- 让二次开发者只要读懂类描述结构,就能快速推断出控件在各种子系统中的行为;
- 为后续的序列化、调试工具、脚本绑定等需求预留扩展空间。
换句话说:8.4 更像是"给 C 写一个类的壳子",9.4 则把这个壳子进化成了一个完整的"元对象中心",这也是 LVGL9 系列在架构上进一步走向"工具友好 / 框架友好"的关键一步。
5 与其它框架的对比与改进思路
5.1 与 AWTK 的对比
- AWTK 同样在 C 语言环境中实现了 Widget 类体系:
- 使用
widget_t+ 虚函数表实现多态; - 通过 type/name 字段和 RTTI 辅助调试与工具化。
- 使用
- LVGL 的类系统与之类似,但更强调:
- 在 class 描述里集中管理"是否可编辑 / 是否默认进 group / 是否继承主题"等行为元信息;
- 属性系统与类描述深度绑定,方便外部工具统一操控属性。
启发:在构建上层框架(如 XSLVGL4.0)时,可以借鉴 AWTK 在 class/type 层面的命名与 RTTI 设计,把 LVGL 的类信息进一步包装成更易于脚本/工具消费的形态。
5.2 与 Qt 的对比
- Qt 使用 C++ 的类与继承机制,配合
Q_OBJECT宏与元对象系统(MOC):- 提供信号/槽、属性系统、类型反射等高级能力;
- 通过
QWidget派生体系实现不同控件的构造、事件与绘制。
- LVGL 的
lv_obj_class可以视为"C 语言版的简化元对象系统":- 构造/析构/事件/绘制的多态通过函数指针完成;
- 属性系统通过 ID + getter/setter 表实现。
启发:对于需要在 LVGL 上构建"脚本友好 UI 框架"的项目,可以在 lv_obj_class 之上再包装一层轻量的元对象系统,用于:
- 给每个类分配稳定的 type id / type name;
- 把属性表暴露给脚本或 UI 编辑器;
- 统一管理信号/事件与回调绑定。
5.3 与 Android / HTML/CSS 的对比
- Android:
- 通过 Java/Kotlin 类体系(
View/ViewGroup)管理界面元素; - XML 布局文件 + 反射/属性系统实现"声明式 UI"与运行时配置修改。
- 通过 Java/Kotlin 类体系(
- HTML/CSS:
- DOM 节点树 + 标签/属性/样式的组合;
- JavaScript 可以通过 DOM API 反射式地操作属性与样式。
与这些体系相比,LVGL 的类系统处于更底层、更偏 C 的位置,但它已经提供了:
- 对象树(类似 DOM / View 树);
- 类描述与属性操作(类似简单版的反射);
- 可编辑性与 group 行为(对应焦点与输入行为的声明式配置)。
这为在 LVGL 之上构建"声明式 UI / 配置驱动 UI / 脚本驱动 UI"打下了基础。
5.4 可能的改进方向
- 从性能角度 :
- 当前类系统的主要开销在于构造链与属性查找,对大部分嵌入式场景是可接受的;
- 在极端性能敏感场景,可以考虑为常用类做一些"扁平化"配置,减少层级过深带来的额外跳转。
- 从代码结构角度 :
- 可以进一步拆分类描述中的"行为元信息"和"属性表",为上层框架暴露更清晰的接口;
- 为类描述增加更明显的调试辅助字段(如 type id、来源模块),方便日志/调试工具输出。
- 从 API 设计角度 :
- 提供一套更友好的"类注册宏",减少手写类描述结构体的样板代码;
- 为自定义控件作者提供专门的文档/模板,指导如何正确设置
base_class、instance_size、editable、group_def等关键字段。
6 小结
lv_obj_class* 是 LVGL9.4 在对象系统层面的一块核心基石:
- 它在 C 语言环境下实现了清晰的类/继承/多态抽象,为各类 widgets、自定义控件以及上层框架提供统一的行为描述入口;
- 通过
editable、group_def、theme_inheritable和属性系统等元信息,它把可编辑性、默认 group 行为、主题继承与属性访问集中到类描述中,方便统一管理与工具化; - 在工程实践中,理解并善用
lv_obj_class,能显著降低维护复杂控件、扩展 LVGL 内核和构建上层 UI 框架时的心智负担。
对于需要在 LVGL 之上搭建更高级 UI 架构(例如 XSLVGL4.0)的项目而言,lv_obj_class 是承上启下的关键抽象:
一端连接底层绘制与对象树,一端支撑属性系统、焦点/主题行为和上层 DSL/编辑器,是值得投入时间深入掌握的模块。