1. Objects
1.1 父部件和子部件
子部件的位置是相对父部件的,父部件移动子部件跟着移动
1 | lv_obj_t * parent = lv_obj_create(lv_scr_act()); /*Create a parent object on the current screen*/ |
部件可以动态创建和删除,删除父部件会递归删除子部件:
1 | lv_obj_t * lv_<widget>_create(lv_obj_t * parent, <other parameters if any>); |
在子部件中删除父部件:
1 | lv_obj_del_async(obj) lv_timer_handler() LV_EVENT_DELETE |
其他删除函数:
1 | lv_obj_clean(obj) // 移除某一父部件的所有子部件 |
1.1.1 从父部件中获取子部件
对于子部件使用
1 | lv_obj_set_parent(obj, new_parent) // 为子部件设置新的父部件 |
对于父部件使用
1 | lv_obj_get_child(parent, idx) // 在父部件中获取子部件 |
- idx 表示子部件加入父部件时的顺序,第一个为 0,第二个为 1,最后一个为 -1
示例:https://docs.lvgl.io/8.3/widgets/extra/list.html#sorting-a-list-using-up-and-down-buttons
2.Screen
屏幕没有父对象
1 | lv_scr_act() |
2.1 Layers
层的概念:如果两个部件部分重叠,那么就产生了“层”的概念,上层部件会显示,下层部件会被遮盖,有以下几种方式来改变部件的层:
lv_obj_move_foreground(obj)lv_obj_move_background(obj)lv_obj_move_to_index(obj, idx),从0到child_num-1对应背景到前景,支持 idx 为负数1
lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
lv_obj_swap(obj1, obj2)lv_obj_set_parent(obj, new_parent),为 obj 设置新的父部件,obj 将会成为新的父部件的前景
两个自动创建的层:
- top layer:总是在所有对象之上,可以用来显示提示窗口,全局菜单
- system layer:在 top layer 之上,强制用来显示系统层面的东西,例如如鼠标指针
吸收点击焦点,只能点击 top_layer 上的控件
1 | lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE); |
3.Parts
4.States
5.snapshot
将一个 LVGL对象中转换为快照图片,或者将快照图片描绘在一个对象上
1 | /** Take snapshot for object with its children. |
目前仅支持以下几种颜色格式:
- LV_IMG_CF_TRUE_COLOR_ALPHA
- LV_IMG_CF_ALPHA_1BIT
- LV_IMG_CF_ALPHA_2BIT
- LV_IMG_CF_ALPHA_4BIT
- LV_IMG_CF_ALPHA_8BIT
注意,如果想要为对象更换一张新的图片,需要先手动释放该对象上的原图片:
1 | void update_snapshot(lv_obj_t * obj, lv_obj_t * img_snapshot) |
2. Positions, sizes, layouts

2.1 设置位置大小
设置位置的几种方法:
lv_obj_set_x(btn, 10):指定坐标lv_obj_set_x(btn, lv_pct(50)):按照父部件的百分比设置lv_obj_set_pos(btn, 10 , 50)
2.2 改变对齐原点 Align
(1)嵌套内情况
1 | lv_obj_set_align(obj, align); |
LV_ALIGN_TOP_LEFTLV_ALIGN_TOP_MIDLV_ALIGN_TOP_RIGHTLV_ALIGN_BOTTOM_LEFTLV_ALIGN_BOTTOM_MIDLV_ALIGN_BOTTOM_RIGHTLV_ALIGN_LEFT_MIDLV_ALIGN_RIGHT_MIDLV_ALIGN_CENTER

特殊的,设置子部件位于父部件的中心:
1 | lv_obj_center(obj); |
如果父部件的大小改变,则使用相对位置的子部件的位置也会自动改变
(2)嵌套外情况
例如,想要一个标签对齐一个图片,这就是嵌套外情况,可以使用:
1 | lv_obj_align_to(obj_to_align, reference_obj, align, x, y); |
可选参考点:
LV_ALIGN_OUT_TOP_LEFTLV_ALIGN_OUT_TOP_MIDLV_ALIGN_OUT_TOP_RIGHTLV_ALIGN_OUT_BOTTOM_LEFTLV_ALIGN_OUT_BOTTOM_MIDLV_ALIGN_OUT_BOTTOM_RIGHTLV_ALIGN_OUT_LEFT_TOPLV_ALIGN_OUT_LEFT_MIDLV_ALIGN_OUT_LEFT_BOTTOMLV_ALIGN_OUT_RIGHT_TOPLV_ALIGN_OUT_RIGHT_MIDLV_ALIGN_OUT_RIGHT_BOTTOM
使用
lv_obj_align_to不会因大小改变而自动对齐
2.2 大小
设置大小的几种方法:
1 | lv_obj_set_width(obj, 200); |
可以指定大小,可以按照比例,也可以使用LV_SIZE_CONTENT,让部件按照其子部件大小来适当的调整自己的大小。
上面是设置边框大小,也可以设置内容大小,边框和内容之间会自动 padding:
1 | lv_obj_set_content_width(obj, 50); //The actual width: padding left + 50 + padding right |
2.3 使用 styles
使用风格有很多好处,上面都是用部件的本地风格去设置的,额外的创建风格,加到部件上:
- 统一设置,统一修改
1 | static lv_style_t style; |
留坑
2.4 平移-Translation
如果想要使得按键在被按下时向上移动一段距离,可以使用:
1 | static lv_style_t style_normal; |
这种移动是真的移动,相关位置会变化的
2.5 变化-Transformation
如果想要使得按键在被按下时变大,可以使用:
1 | static lv_style_t style_pressed; |
transform 会在原有尺寸上两侧各加上 10pt;注意,这种变化是一种视觉变化,并不是真的变大了
2.6 布局-Layout
1 | lv_obj_set_layout(obj, <LAYOUT_NAME>) |
将部件添加到一个布局中,会覆盖掉部件本身的大小、位置。
LVGL 支持量子布局:
- Flexbox
- Grid
(1) object 的 Flags
LV_OBJ_FLAG_HIDDEN添加隐藏属性,该 object 会被布局忽略LV_OBJ_FLAG_IGNORE_LAYOUT该部件会被布局忽略LV_OBJ_FLAG_FLOATING部件会被布局忽略,并且不受LV_SIZE_CONTENT的改变
3. style
- 多个样式可以赋给同一个部件,多个部件也可以使用相同的 style
- LVGL 会搜索一个特性,直到某个 style 定义了它,如果没有就使用默认值
- 如果未定义子部件的属性,子部件会从父部件继承该属性,这比默认值优先级更高
- 可以设置 state 改变时,改变 style
3.1 States
所有可能的状态如下:
state 应用规则:
- 数值越大,优先级越高,优先级越高,就显示对应 state 的 style
- 例如
LV_STATE_FOCUSED | LV_STATE_PRESSED则对应数值为二者之和 - 如果优先级最高的 state 没有设置 style 则显示默认状态的 style
为部件添加以及获取 States 的函数如下:
1 | void lv_obj_add_state(lv_obj_t * obj, lv_state_t state); |
3.2 Parts
基本组成部分:Styles — LVGL documentation
3.3 使用 Style
(1) Style 本身
Style 必须是 static 或者动态内存分配的,不能是局部变量;并且使用之前需要调用lv_style_init初始化:
1 | static lv_style_t style_btn; |
移除 Style 中的某个属性:
1 | lv_style_remove_prop(&style, LV_STYLE_BG_COLOR); |
获取 Style 中的某个属性:
1 | lv_style_value_t v; |
重置 Style(会释放其中所有属性的内存):
1 | lv_style_reset(&style); |
(2) Style 和 obj
给某个部件的 part 在某种 state 下添加 style:
1 | lv_obj_add_style(btn, &btn_red, LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED); |
- part 和 state 都是
selector可以设置的参数
移除某个部件的所有 Style:
1 | lv_obj_remove_style_all(obj) |
具体移除某个部件的 Style:
1 | lv_obj_remove_style(obj, style, selector) |
- 移除
style必须在函数的selector匹配设置style时使用的selector style设置为 NULL,则仅进行selector的匹配selector可以使用LV_STATE_ANY和LV_PART_ANY来匹配任意 state 和 part
获取 obj 的某个 property 的值:
1 | lv_obj_get_style_<property_name>(obj, <part>) |
更新了 Style ,那已经设置好的 obj 怎么更新:Styles — LVGL documentation
(3) 转换效果-Transitions
当 obj 的 state 改变时,可以添加一个转场效果,主要设置如下:
- 转场所需时间
- 延迟多少 ms 之后开始转场
- 转场动画路径
- 哪些属性需要动画改变
示例如下:
1 | /*Only its pointer is saved so must static, global or dynamically allocated */ |
(4) 透明度(opa)、混合模式(blend mode)、角度(angle)、缩放(zoom)
这些属性设置时,LVGL会创建父部件及其子部件的 snapshot,这涉及是否有足够内存来使用,参考:Styles — LVGL documentation
设置选择角度或者缩放比例:
1 | lv_obj_set_style_transform_angle(btn, 150, 0); /*15 deg*/ |
- 角度:
- 缩放:
3.4 Themes
主题是 Style 的集合,启用主题会自动应用于所有创建的部件,使用主题的方式:
1 | lv_theme_t * th = lv_theme_default_init( |
在文件lv_conf.h 设置 LV_USE_THEME_DEFAULT 1 LVGL会自动使用默认主题。
嵌套扩展主题:链接
4. 滚动-Scroll
当子部件超过了父部件的大小,就会产生滚动,例如滚动查看文本框中的文字。
参考:链接
5. Events
为一个部件添加事件响应:
1 | lv_obj_t * btn = lv_btn_create(lv_scr_act()); |
移除事件响应的两种方式:
1 | lv_obj_remove_event_cb(obj, event_cb) // 根据回调函数 |
5.1 事件类型
- Input device events:通用事件
- Drawing events:通用事件
- Other events:通用事件
- Special events:特殊部件才有的
- Custom events:自定义事件
详细事件种类:链接
(1) 自定义事件
需要在 LVGL 中注册一个 id,之后往这个 id 中手动发送事件就行:
1 | uint32_t MY_EVENT_1 = lv_event_register_id(); // 注册一个自定义事件码 |
(2)手动发送事件
函数原型:
1 | lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param) |
示例代码:
1 | /*Simulate the press of the first button (indexes start from zero)*/ |
(3) 刷新事件
LV_EVENT_REFRESH 用来让用户通知一个部件,让它刷新自己,可能的使用场景:
- label 的文本更新了,需要刷新
- 系统语言改变了,label 需要刷新
- 在某些情况下使能按键,需要让按键刷新
(4) 回调函数的参数
lv_event_t *e 中包括很多有用的信息:
lv_event_get_code(e)获取事件码lv_event_get_current_target(e)获取触发事件的对象lv_event_get_target(e)获取原始触发事件的对象;如果不使能event bubbling功能,它和上面的函数获取的一样,反之,当子部件触发事件,对于父部件使用该函数会获取到子部件而非自己本身lv_event_get_user_data(e)获取注册回调函数lv_obj_add_event_cb时传递的参数lv_event_get_param(e)获取lv_event_send函数传递的参数
(5) Event bubbling
使用如下函数可以为 obj 添加 event bubbling 标志:
1 | lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE) |
含有该标志的 obj 会上传触发的事件到其父部件上,如果父部件同样有该标志,会继续传递给更上一层。
6. 输入设备
6.1 指针
6.1.1 光标
1 | lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv); |
6.1.2 手势-Gestures
例如左滑和右滑,LVGL提供了基础的手势识别(部件滚动不算手势)。大部分部件默认会将手势事件 LV_EVENT_GESTRUE 上传给其父部件,最终上传到屏幕对象上,例如:
1 | void my_event(lv_event_t * e) |
想要阻止上传 LV_EVENT_GESTRUE 需要清楚标志 LV_OBJ_FLAG_GESTURE_BUBBLE:
1 | lv_obj_clear_flag(obj, LV_OBJ_FLAG_GESTURE_BUBBLE) |
6.2 键盘和编码器
6.2.1 Group
需要将一个输入设备和一个组关联在一起,一个输入设备只能关联一个组,一个组中可以关联多个输入设备。
创建组,并为组添加对象:
1 | lv_group_t * g = lv_group_create() |
将输入设备和组进行关联:
1 | lv_indev_set_group(indev, g) |
例如,一个编码器输入设备的创建过程如下:
1 | static lv_indev_drv_t indev_drv_3; |
重点是输入设备的回调函数,例如 PC 模拟器上的鼠标编码器:
1 | void sdl_mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) |
这个函数是和物理设备交互的关键函数,对于编码器而言,需要设置当前的编码值以及按键的状态两个成员参数;
该函数会在 LVGL 的定时任务中调用:
1 | if(indev->driver->read_cb) { |
调用 read_cb 取得的数据会存储在 data 中,在定时任务的后半部中会根据输入设备的类型进行处理,例如对于编码器有:
1 | if(data->enc_diff < 0) { |
其他输入设备的 data 可以查看 lv_indev_data_t 这个结构体的成员:
1 | typedef struct { |
常见的一些输入设备中,例如触摸屏需要设置 point 成员,键盘需要设置 key 成员,编码器需要设置 enc_diff 成员。
6.2.2 编辑和导航模式
因为编码器的按键有限,所以 LVGL 定义了两种模式:编辑 (edit) 和 导航 (navigate),长按编码器来进行这两种模式的切换:
- 编辑模式下转动编码器会给所属组发送
LV_KEY_LEFT/LV_KEY_RIGHT键值 - 导航模式下转动编码器会给所属组发送
LV_KEY_NEXT/LV_KEY_PREV键值
短按编码器可以聚焦 (focus) 对象,对象状态为 LV_STATE_FOCUSED;如果对象进入编辑模式,对象状态为 LV_STATE_FOCUSED | LV_STATE_EDITED ,对于不同状态可以设置不同的 style
6.2.3 默认组
常用部件会自动添加到默认组中,不再需要手动调用lv_group_add_obj函数,我们可以创建一个组,然后将其设置为默认组:
1 | lv_group_t * g = lv_group_create(); |
哪些部件会被自动添加呢?部件的属性 group_def 为 LV_OBJ_CLASS_GROUP_DEF_TRUE 会被自动添加,例如:
1 | const lv_obj_class_t lv_btn_class = { |
7. 颜色-Colors
在 lv_conf.h 中可以配置两个和颜色有关的宏:
1 | /*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ |
LV_COLOR_16_SWAP 为 0 表示小字节序,通常来说都是小字节序;如果显示器需要大字节序则需要设置为 1
7.1 创建颜色的方式
7.1.1 手动创建
1 | //All channels are 0-255 |
7.1.2 从调色板中衍生
1 | lv_color_t c = lv_palette_main(LV_PALETTE_BLUE) |
7.1.3 修改和混合颜色
1 | // Lighten a color. 0: no change, 255: white |
7.2 不透明度-Opacity
LV_OPA_TRANSP 值为 0 ,代表透明;
LV_OPA_COVER值为255,代表完全不透明;
LV_OPA_50 代表半透明
8. 字体-fonts
需要在 lv_conf.h 中注意的设置:
1 | // 1.颜色通道顺序 |
LVGL 支持 UTF-8 格式的 Unicode 字符,所以我们的文件也需要保存为 UTF-8 编码。
8.1 内建字体
LVGL 提供了 Montserrat 风格的不同字体大小的内建字体,具体参见:链接,使用 style 来设置:
1 | lv_style_set_text_font(&my_style, &lv_font_montserrat_28); /* 设置字号为 28 */ |
此外,LVGL 还提供了大量内建特殊字符图形:

使用示例如下(编译时会自动拼接):
1 | lv_label_set_text(my_label, LV_SYMBOL_OK "Apply" LV_SYMBOL_PLAY); |
8.2 其他字体特征
8.3 添加中文字库
找到一款开源字体文件:链接
查找需要转换的字符的 Unicode 编码范围:链接
1
2// 常用汉字,ASCLL,标点符号
0x4E00-0x9FA5,0x9FA6-0x9FFF,0x30-0x39,0x61-0x7a,0x41-0x5a,0x20-0x2F,0x3A-0x40,0x5B-0x60,0x7B-0x7E在官方的在线字体转换网站上转换得到字符的 bitmap:[链接](Online font converter - TTF or WOFF fonts to C array | LVGL)
将生成的 .c 文件添加到路径
lvgl/src/font/目录下
方法1-局部使用字库(推荐)
1 | LV_FONT_DECLARE(lv_font_siyuan_16); |
方法2-全局使用字库(不推荐)
在 lv_conf.h 中声明:
1 | define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(lv_font_siyuan_16) |
8.4 添加新的符号
为某个符号制作 .c 文件,并添加到工程中的过程和 8.3 类似,但使用时有所不同:
将符号的 Unicode 值转换为 UTF-8 编码,例如
0xf287 -> EF 8A 87宏定义:
\#define MY_USB_SYMBOL "\xEF\x8A\x87"为风格添加字体,然后才能为 label 使用符号(符号相当于仅有一个字符的字库):
1
2
3lv_style_set_text_font(&style, &lv_symbol_usb );
lv_obj_add_style(label1, &style, 0);
lv_label_set_text(label, MY_USB_SYMBOL);
符号获取网站:链接
Unicode 转 UTF-8 网站:链接
8.5 添加新的字符引擎
例如 FreeType,在运行时动态渲染,TODO。
10 Image
10.1 显示内建格式的图片
在线图片转换器:https://lvgl.io/tools/imageconverter
显示 .c 文件的原始图片:
1 | LV_IMG_DECLARE(my_icon_dsc) |
10.2 使用 File System 和显示原始格式的图片
TODO
11. Animations
11.1 基本使用
动画的使用还是比较简单的:
1 | static void anim_size_cb(void * var, int32_t v) |
不同动画效果参见:链接,删除一个动画效果,传入部件以及对应属性的设置函数来完成:
1 | bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) |
11.2 Timeline
TODO
12. Timer
LVGL 中 Timer 的实现是非抢占式的,手动创建的 Timer 会被 lv_timer_handler() 调用,因此 lv_timer_handler() 需要每隔几毫秒调用一次。
创建一个 Timer:
1 | lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data) |
period 的单位是 毫秒,user_data 不能是局部变量。例如:
1 | void my_timer(lv_timer_t * timer) |
创建完一个定时器之后,每隔 period 毫秒之后都会运行一次。
其他相关函数:
1 | lv_timer_ready(timer) // 在下一次调用 lv_timer_handler()时即刻运行回调函数 |
12.1 闲置时间
通过调用如下函数可以获取 lv_timer_handler() 在上一次的调用中的空闲时间所占百分比:
1 | uint8_t lv_timer_get_idle(void) |
基于该值可以合理设置 lv_timer_handler() 的调用间隔。
简单
基础
图形化编程
输入组
移植