本帖最后由 WT_0213 于 2024-1-24 09:39 编辑
来自LVGL官方的介绍
官网地址:https://lvgl.io/
LVGL是最受欢迎的免费开源嵌入式图形库,可为任何MCU、MPU和显示器类型创建漂亮的UI。
使用我们的拖放UI编辑器SquareLine Studio来简化开发。
可移植性
开源
LVGL是完全开源的,有几个优点。
首先,它使您可以控制库,因为您不仅可以查看、修改、编译和调试底层源代码,还可以完全获取它。一旦你下载了它,它就是你的了。这种独立于单一供应商的独立性是一个巨大的价值。
除此之外,开源鼓励协作和知识共享。世界各地的开发人员都为改进软件做出了贡献,使其更可靠、功能更丰富,以解决现实生活中的问题。
自由
LVGL 在 MIT 许可下分发,允许用户自由使用、修改和分发软件,而不会施加复杂的限制或限制。
它为开发人员和企业提供了将软件整合到他们的项目中的灵活性,即使是出于商业目的,同时保持原作者的归属。
最低要求
LVGL 可在任何现代 MCU 或 MPU 上运行。* 架构:16、32 或 64 位
- 时钟速度:> 64MHz
- RAM:4kB + 150byte /小部件(对于具有几个屏幕的UI~48kB)
- 闪存:~100kB(LVGL)(取决于启用的功能)
- 绘制缓冲区:> 1/10 屏幕大小的缓冲区用于渲染
- 帧缓冲器:显示控制器、内部或外部RAM中至少有1个帧缓冲器
- 编译器:C99 或更高版本
- 构建系统:LVGL 没有外部依赖。只需将其复制到您的项目中,并将其与项目的其他文件一起编译即可
UI 功能
许多小部件
LVGL 带有 30+ 内置小部件,例如弧形、条形、日历、图表、复选框、下拉列表、键盘、仪表、消息框、开关、表格、选项卡视图、文本区域。
有了这些小部件,您在智能手机应用程序中看到的内容也可以通过 LVGL 实现。
这些小部件可以实时创建和删除。这样,您可以通过动态创建当前可见的屏幕和小部件来节省 RAM。
渲染功能
LVGL 带有强大的软件渲染引擎,可以使用最少的资源以矢量图形方式绘制抗锯齿小部件。
- 半径矩形
- 边框与半径
- 水平梯度和垂直梯度
- 框阴影
- 歪斜线
- 弧
- 缩放和旋转图像或任何小部件
- 掩蔽
风格
您可以从 100+ 样式属性中进行选择,以设置小部件运行时的样式。它可以动态更改 UI 的主题,甚至对样式进行动画处理。它还可以节省闪光灯,因为您需要使用更少的图像。
小部件可以在任何状态下设置样式,例如按下、选中、聚焦、禁用。小部件的任何部分也可以自定义。例如,滑块由主、指示灯和旋钮部分组成,其外观可以自由调节。
最重要的是,通过使用您所做的过渡,LVGL 可以在状态更改时自动为样式制作动画。
LVGL的初始化
在使用LVGL之前前,你需要调用以下三个个函数完成LVGL库的初始化以及LVGL显示设备接口的初始化,其中lv_init()与lv_port_disp_init()方法必须添加,lv_port_indev_init()用于触摸:
// lvgl 初始化
lv_init();
// 初始化显示器和创建用于绘图的缓冲区
lv_port_disp_init();
//找到 LittelvGL 支持的输入设备
lv_port_indev_init();
然后我们就可以,写我们第一个LVGL的应用“Hello world”:
/*更改活动屏幕的背景颜色*/
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
/*创建一个白色标签,设置其文本并将其与中心对齐*/
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello world");
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
要处理 LVGL 的任务,您需要定期调用以下命令之一:
lv_task_handler()
- main() 函数的 while(1)
- 定时器周期性中断(低优先级
lv_tick_inc()
)
- 定期执行操作系统任务
时间并不重要,但应该大约是 5 毫秒,以保持系统响应。
while(1) {
lv_task_handler();
usleep(5 * 1000);
//bflb_mtimer_delay_ms(5);
}
然后将编译得到的结果使用模拟器打开,就可以在窗口上看到带有“Hello world”字样的蓝色窗口:
![屏幕截图 2023-12-11 170638.png 屏幕截图2023-12-11170638.png](data/attachment/forum/202312/11/170807w2209d99w4ok4kdo.png)
LVGL输入设备的类型
如果只有显示设备没有输入的话,那么用户就没办法交互了。所以输入设备必不可少,下面介绍一下LVGL输入设备。
LVGL 所支持的输入设备有以下几种:
- 1、触摸板或鼠标
- 2、键盘或小键盘
- 3、具有左/右转和推动选项的编码器
- 4、外部按钮
要注册输入设备,必须初始化一个变量:lv_indev_drv_t
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*基本初始化*/
indev_drv.type =... /*见type介绍*/
indev_drv.read_cb =... /*见read_cb 介绍*/
/*在 LVGL 中注册驱动程序并保存创建的输入设备对象*/
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
type
可以是
LV_INDEV_TYPE_POINTER
触摸板或鼠标
LV_INDEV_TYPE_KEYPAD
键盘或小键盘
">LV_INDEV_TYPE_ENCODER
具有左/右转和推动选项的编码器
LV_INDEV_TYPE_BUTTON
外部按钮几乎按压屏幕
b
是一个函数指针,将定期调用它来报告输入设备的当前状态。
触摸板、鼠标或任何指针
可以点击屏幕上的点的输入设备属于此类别。
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_input_read;
...
void my_input_read(lv_indev_drv_t * drv, lv_indev_data_t*data)
{
if(touchpad_pressed) {
data->point.x = touchpad_x;
data->point.y = touchpad_y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
小键盘或键盘
带有所有字母的完整键盘或带有几个导航按钮的简单小键盘都属于这里。
要使用键盘/小键盘:
- 使用 type 注册函数。
read_cb LV_INDEV_TYPE_KEYPAD
- 必须创建一个对象组:
lv_group_t * g = lv_group_create() 并且必须使用 lv_group_add_obj(g,obj)
- 必须将创建的组分配给输入设备:(
my_indev
是 ` `lv_indev_drv_register 的返回值 )
- 使用
LV_KEY_...
在组中的对象之间进行导航。可用的KEY可以查看 lv_core/lv_group.h
-
-
indev_drv.type = LV_INDEV_TYPE_KEYPAD;
indev_drv.read_cb = keyboard_read;
...
void keyboard_read(lv_indev_drv_t * drv, lv_indev_data_t*data){
data->key = last_key(); /*获取上次按下或释放的键*/
if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED;
else data->state = LV_INDEV_STATE_RELEASED;
}
编码器
使用编码器,您可以做 4 件事:
- 按下它的按钮
- 长按其按钮
- 左转
简而言之,编码器输入设备的工作方式如下:
- 通过转动编码器,您可以专注于下一个/上一个对象。
- 当您在简单对象(如按钮)上按下编码器时,它将被单击。
- 如果在复杂对象(如列表、消息框等)上按下编码器,则该对象将进入编辑模式,转动编码器即可在对象内导航。
- 要离开编辑模式,请长按按钮。
要使用编码器(类似于键盘),应将对象添加到组中。
indev_drv.type = LV_INDEV_TYPE_ENCODER;
indev_drv.read_cb = encoder_read;
...
void encoder_read(lv_indev_drv_t * drv, lv_indev_data_t*data){
data->enc_diff = enc_get_new_moves();
if(enc_pressed()) data->state = LV_INDEV_STATE_PRESSED;
else data->state = LV_INDEV_STATE_RELEASED;
}
按钮
按钮是指屏幕旁边的外部“硬件”按钮,这些按钮被分配给屏幕的特定坐标。 如果按下按钮,它将模拟按下指定的坐标。(类似于触摸板)
lv_indev_set_button_points(my_indev,points_array)
points_array const lv_point_t points_array[] ={{12,30},{60,90},...}
indev_drv.type = LV_INDEV_TYPE_BUTTON;
indev_drv.read_cb = button_read;
...
void button_read(lv_indev_drv_t * drv, lv_indev_data_t*data){
static uint32_t last_btn = 0; /*存储上次按下的按钮*/
int btn_pr = my_btn_read(); /*获取 (0,1,2...) 按下按钮 ID */
if(btn_pr >= 0) { /*判断按钮是否按下 ( -1 没有按下按钮)*/
last_btn = btn_pr; /*保存按下按钮的 ID */
data->state = LV_INDEV_STATE_PRESSED; /*设置按钮按下状态*/
} else {
data->state = LV_INDEV_STATE_RELEASED; /*设置按钮释放状态*/
}
data->btn = last_btn; /*保存按钮*/
}
使用LVGL模拟器
对于单片机或者MCU来说使用LVGL图形库,进行开发如果每次改动都通过编译和烧录进行效果确认,那么是非常耗时的一个操作。工作效率也很低,而且比较繁琐。那么有没有一种方式可以直接预览画面而不进行烧录和编译进行效果的查看呢,答案是肯定的 LVGL 为我们提供了PC端的模拟器,它可以在 PC 端上直接生成可交互的界面,无需编译烧录即可查看绘制效果。
![屏幕截图 2023-12-11 175216.png 屏幕截图2023-12-11175216.png](data/attachment/forum/202312/11/175312bhycvgddyj4j4rcd.png)
如图,这个就是模拟器上面运行的效果。
后面会单独写一篇关于模拟器配置的教程
核心部件
LVGL主要部件包含以下几种
圆弧 (lv_arc)
标题栏 (lv_bar)
按钮 (lv_btn)
![屏幕截图 2023-12-11 175955.png 屏幕截图2023-12-11175955.png](data/attachment/forum/202312/11/180013v2mrse4ok0tkexmx.png)
按钮矩阵 (lv_btnmatrix)
![屏幕截图 2023-12-11 180241.png 屏幕截图2023-12-11180241.png](data/attachment/forum/202312/11/180300sce3gdp4e2zs0kkz.png)
画布(lv_canvas)
复选框 (lv_checkbox)
![屏幕截图 2023-12-11 180139.png 屏幕截图2023-12-11180139.png](https://bbs.ai-thinker.com/data/attachment/forum/202312/11/180159nhubfkb21k1fvx1l.png)
下拉列表 (lv_dropdown)
![屏幕截图 2023-12-11 180738.png 屏幕截图2023-12-11180738.png](data/attachment/forum/202312/11/180753curhc5dvh0lvtf5z.png)
图像 (lv_img)
标签 (lv_label)
![屏幕截图 2023-12-11 180045.png 屏幕截图2023-12-11180045.png](data/attachment/forum/202312/11/180114sw5n68ww6i4sd059.png)
线 (lv_line)
滚动 (lv_roller)
滑块 (lv_slider)
开关 (lv_switch)
![屏幕截图 2023-12-11 175901.png 屏幕截图2023-12-11175901.png](data/attachment/forum/202312/11/175924mc2u7bjbbefff7gc.png)
表 (lv_table)
![屏幕截图 2023-12-11 180504.png 屏幕截图2023-12-11180504.png](data/attachment/forum/202312/11/180518ijrwqdkxw9w33eqn.png)
文本区域 (lv_textarea)
![屏幕截图 2023-12-11 180412.png 屏幕截图2023-12-11180412.png](data/attachment/forum/202312/11/180427ua4y47yx73fpy77y.png)
下面由简到繁讲解以下基础控件
标签
标签(label)是 LVGL 基础的控件之一。标签的作用就是显示一些文字。接下来通过介绍标签来介绍 LVGL 控件的创建、布局与设置属性。
标签的创建
通过以下函数可以创建一个标签:
lv_obj_t* lv_label_create(lv_obj_t* parent);
lv_obj_t
是 LVGL 所有控件的通用类型,包括根窗体在内的所有控件都使用该结构描述。
参数 parent
指定了标签需要被放在哪一个父容器中。由于一个较大的项目内会存在许多控件,因此往往需要将一个较大的窗口划分为若干结构,每一个结构放入用途相似的的控件,使用户更易熟悉如何操作。例如,一个文本编辑器窗口可能会按功能分为顶层菜单栏、侧边导航栏、底部状态栏以及中间的编辑区,每个区域的控件都可以安排在各栏内统一调整。
最基本的父容器就是整个显示屏窗口对象,可以使用 lv_scr_act()
函数获取当前的窗口对象。操作系统上的窗口可以设置一些属性,例如窗口大小、标题文字、图标等,不过嵌入式屏幕往往是固定的,因此窗口对象一般只作控件的父容器使用。
使用以下代码就可以在当前窗口中创建一个标签了:
lv_obj_t* label01 = lv_label_create(lv_scr_act());
创建得到的标签没有任何可显示的内容,可以调用 lv_label_set_text()
为标签添加上文字:
lv_label_set_text(label01, "Hello, world!");
这样就可以在屏幕中显示一些文本了。LVGL 支持直接显示 Unicode 文字,只要在源文件使用 UTF-8 编码即可。如果要显示变量的值,LVGL 也提供了 lv_label_set_text_fmt()
函数,可以直接格式化文本。
接下来编译工程并下载,就可以看到显示的效果了:
![2829890-20220614165436768-618057708.jpg 2829890-20220614165436768-618057708.jpg](data/attachment/forum/202312/10/203134fohdx5yh1xwrq5d1.jpg)
标签的布局
以上创建的标签默认放在屏幕的左上角,并且如果创建多个标签等控件,它们都会被重叠放置在左上角。如果需要将控件安排到合适的位置,就需要安排它们的布局。一般情况下,可以用以下函数重新调整一个控件的布局:
void lv_obj_align(lv_obj_t* obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
align
指定了控件的对齐方式,可以检查枚举类型 lv_align_t
来获取支持的对齐方式。x_ofs
和 y_ofs
是对齐后的额外偏移量,正值表示额外向右下偏移。
LVGL 包含了许多枚举类型,如果不知道该如何传值,可以查看头文件包含的枚举值。
和大多数 GUI 库一样,屏幕的左上角为坐标原点 (0, 0) ,往右为 x 轴正向,往下为 y 轴正向,坐标的单位为像素或分辨率。
例如,如果额外给以上标签添加对齐:
lv_obj_align(label01, LV_ALIGN_CENTER, 0, -30);
那么它就会出现在屏幕中间向上 30 像素的位置:
![2829890-20220614165510296-1645161539.jpg 2829890-20220614165510296-1645161539.jpg](data/attachment/forum/202312/10/203144vigrj2hij375lll1.jpg)
如果要创建更灵活的布局,可以使用 lv_obj_create()
创建一个基本对象。这种直接创建的基本对象一般用作框架,然后通过嵌套框架的形式组织对齐,例如:
/* outer widget align */
lv_obj_t* cont_top = lv_obj_create(lv_scr_act());
lv_obj_t* cont_bottom = lv_obj_create(lv_scr_act());
lv_obj_align(cont_top, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_align(cont_bottom, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
/* inner widget align */
lv_obj_t* label_top = lv_label_create(cont_top);
lv_label_set_text(label_top, "At Top Left");
lv_obj_align(label_top, LV_ALIGN_CENTER, 0, 0);
lv_obj_t* label_bottom = lv_label_create(cont_bottom);
lv_label_set_text(label_bottom, "At Bottom Right");
lv_obj_align(label_bottom, LV_ALIGN_CENTER, 0, 0);
这里先将外层的框架在屏幕上对齐,然后再在框内创建标签,让标签在框架内对齐。效果为:
![2829890-20220614165523104-866871228.jpg 2829890-20220614165523104-866871228.jpg](data/attachment/forum/202312/10/203159jheihdmhcbczzcrw.jpg)
通过这种嵌套的对齐方式,可以先让一些基础控件在框架内对齐,然后再让框架之间相对对齐。这种对齐方式更灵活,而且方便日后调整各个控件的相对位置。
LVGL 的所有控件都是以这种相对位置的形式组织的。官方文档提供了一张图片,可以很清楚地描述所有的相对对齐方式:
![2829890-20220614165537415-315855100.png 2829890-20220614165537415-315855100.png](data/attachment/forum/202312/10/203208gcr6jocc8yc89pjc.png)
由于居中对齐经常用到,可以直接使用 lv_obj_center(*obj*)
函数设置无偏移的居中对齐。
默认的基本控件是有样式的,并且注意到它们长宽都是固定的,如果包含的控件过长,它还会提供一个滚动条。如果需要调整控件的尺寸,可以使用函数,lv_obj_set_width()
和 lv_obj_set_height()
分别调整长宽,或使用 lv_obj_set_size()
一并调整:
lv_obj_t* cont = lv_obj_create(lv_scr_act());
lv_obj_t* label = lv_label_create(cont);
lv_label_set_text(label, "Helllllo, world!");
lv_obj_set_size(cont, 160, 50);
lv_obj_center(cont);
lv_obj_center(label);
![2829890-20220614165553123-239830498.jpg 2829890-20220614165553123-239830498.jpg](data/attachment/forum/202312/10/203224v9fmrz9frsappdes.jpg)
所有的控件都具有宽度和高度基本属性,因此这几个函数对任意的控件都有效。
标签的长模式和颜色调整
框架包含的控件过长会提供一个滚动条,确保包含的内容都可见。标签在创建时,它的宽度会适应包含文本的宽度。如果给一个标签重新调整尺寸,使得它的宽度小于文本的宽度,那么它包含的文本就会自动折叠:
lv_obj_t* label01 = lv_label_create(lv_scr_act());
lv_label_set_text(label01, "A very loooooooooooooooong text");
lv_obj_set_width(label01, 100);
![2829890-20220614165605301-1240591742.jpg 2829890-20220614165605301-1240591742.jpg](data/attachment/forum/202312/10/203238slz7yd0cjac5k8qv.jpg)
如果文本确实过长,超过了标签的长宽极限,那么可以使用函数
void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode);
给标签设置一个长模式。标签一共有 5 种长模式,每种模式的表现形式如下:
枚举值 |
说明 |
LV_LABEL_LONG_WRAP |
将过宽的文本换行,以多行的方式显示所有文本 |
LV_LABEL_LONG_DOT |
将过长的文本隐藏并以省略号代替 |
LV_LABEL_LONG_SCROLL |
将文本来回滚动显示 |
LV_LABEL_LONG_SCROLL_CIRCULAR |
将文本循环滚动显示 |
LV_LABEL_LONG_CLIP |
去除过长部分的文本 |
如果文本显示时有多行,那么可以使用
void lv_obj_set_style_text_align(lv_obj_t* obj, lv_text_align_t value, lv_style_selector_t selector);
将文本垂直对齐。第三个参数 selector
是设置样式用的,这里可以暂时不用理会。
以下动图展示了三种长模式:显示省略号、换行并居中对齐,以及循环滚动:
![2829890-20220614165618273-970812447.gif 2829890-20220614165618273-970812447.gif](data/attachment/forum/202312/10/203251k8izvuqw65havgzv.gif)
需要注意的是,除了滚动以外的其它模式如果没有明确高度,都会在文本过长时优先尝试调整标签高度。
滚动是一种特殊的动画,在后续介绍到动画时还可以创建更丰富的动画效果,可以自行调整文本的滚动行为。
标签的文本可以改变颜色。LVGL 里,调整颜色是通过特殊格式的文本作用的。为了改变颜色,首先需要启用这一模式:
lv_label_set_recolor(label01, true);
重新调整颜色的文本格式为:
#RRGGBB text#
这样 text
对应的文本就会显示为 #RRGGBB
对应的色值。如果屏幕使用的是 16bit 的颜色也不要紧,LVGL 会自动转换颜色。
例如:
lv_label_set_text(label01, "#0000ff Re-color# #ff00ff text# #ff0000 of a# label.");
显示效果为:
![2829890-20220614165645974-447037900.jpg 2829890-20220614165645974-447037900.jpg](data/attachment/forum/202312/10/203302zag5gggnnmdjfh4w.jpg)
按钮
按钮(button)也是一个比较基础的控件。按钮除了可以显示一些提示文字外,还可以点击并获取响应。接下来通过介绍按钮来介绍为控件绑定事件的一般方式。
按钮的创建和事件绑定
按钮的创建和布局方式都与标签类似:
lv_obj_t* btn01 = lv_btn_create(lv_scr_act());
lv_obj_align(btn01, LV_ALIGN_CENTER, 0, -40);
但是注意,创建得到的按钮只是一个简单的形状。为了给它添加说明文本,需要在其中创建一个标签:
lv_obj_t* label01 = lv_label_create(btn01);
lv_label_set_text(label01, "Button");
lv_obj_center(label01);
显示的效果为:
![2829890-20220614165722645-2130466755.jpg 2829890-20220614165722645-2130466755.jpg](data/attachment/forum/202312/10/203313cqdanamqptlldrrz.jpg)
按钮不同于框架,按钮会自动调整宽高来适应其包含的标签大小。
创建的按钮已经默认具有点击动画,不过还无法对点击作出回应。接下来需要给按钮添加回调函数。可以使用以下函数为按钮绑定回调函数:
lv_obj_add_event_cb(lv_obj_t* obj, lv_event_cb_t event_cb, lv_event_code_t filter, void* user_data);
任意可交互控件都可以使用该函数添加回调函数。这里不用管该函数的返回值。event_cb
是事件的回调函数,filter
决定按钮会对哪些事件作出响应,可以在 user_data
传入一些自定义的数据。
检查类型 lv_event_cb_t
的定义就可以明白如何编写回调函数。回调函数有且仅有一个 lv_event_t
类型的参数。该类型是一个比较复杂的结构类型,目前只需要明白它包括的结构成员包括自定义数据 user_data
即可。
例如,以下创建了一个简单的回调函数:
static void button_clicked_cb(lv_event_t* e) {
static uint8_t count = 0;
count++;
lv_label_set_text_fmt((lv_obj_t*)e->user_data, "Clicked: %d", count);
}
这里通过自定义参数来修改外部标签的文本。那么在绑定时,就需要这样传入参数:
lv_obj_add_event_cb(btn01, button_simple_cb, LV_EVENT_CLICKED, label01);
![2829890-20220614165742597-2124628895.gif 2829890-20220614165742597-2124628895.gif](data/attachment/forum/202312/10/203323txw1bhzqcoprch3e.gif)
这里让按钮只对点击事件产生响应。如果要让按钮对多个事件响应的话,需要先让按钮对所有事件 LV_EVENT_ALL
产生响应的话,然后在回调函数内进一步判断事件类型:
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
/* ... event handler ... */
}
这就像在中断函数内判断中断源一样。
不过以上回调还可以使用另一种不传入用户参数的形式完成。首先,通过
lv_obj_t* lv_event_get_target(lv_event_t* e);
可以获取产生事件的控件,然后通过
lv_obj_t* lv_obj_get_child(const lv_obj_t* obj, int32_t id);
获取该控件的子控件。在创建控件时,需要传入父容器控件,创建时父容器也会通过 id
记录包含的子控件,创建最早的控件 id 就是 0 ,第二早的 id 是 1 ,最晚的 id 还可以表示为 -1 等。这样就可以在事件回调函数内获取被点击按钮的标签控件对象了。
控件的通用行为
LVGL 中,可以通过
void lv_obj_add_flag(lv_obj_t* obj, lv_obj_flag_t f);
为控件设置一些通用的标志,来改变控件的行为。
例如,以上按钮都是普遍的按钮,它们通过点击来触发响应。但是还有一部分按钮,像控制键是通过点击来切换启用/关闭状态的。那么此时就可以给按钮添加一个这样的标志:
lv_obj_t* btn02 = lv_btn_create(lv_scr_act());
lv_obj_add_flag(btn02, LV_OBJ_FLAG_CHECKABLE);
这样创建的按钮可以对 LV_EVENT_VALUE_CHANGED
这个特殊的事件响应,而普通的按钮不行。不仅如此,切换之后的部分样式也会发生改变:
![2829890-20220614165807706-1819967881.gif 2829890-20220614165807706-1819967881.gif](https://bbs.ai-thinker.com/data/attachment/forum/202312/10/203336uu69biu4ymse2eux.gif)
可以给一个控件添加多个标志,只需要使用按位或运算符 |
连接起来即可。还可以清除一个控件的标志。例如,如果给一个框架清除可滚动的标志,那么当它包含长文本时就不再可以滚动显示全部内容:
lv_obj_t* cont = lv_obj_create(lv_scr_act());
lv_obj_t* label = lv_label_create(cont);
lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
lv_label_set_text(label, "A label contains very long text");
lv_obj_set_size(cont, 160, 50);
效果为:
![](https://img2022.cnblogs.com/blog/2829890/202206/2829890-20220614165901045-482945402.jpg)
![2829890-20220614165901045-482945402.jpg 2829890-20220614165901045-482945402.jpg](data/attachment/forum/202312/10/203505wssg3dhp4ppgkotz.jpg)
标志是一个很重要的内容,通过为控件加上各种标志,可以自定义更多抽象的控件类型。例如,具有 LV_OBJ_FLAG_CLICKABLE
标志的控件可以响应点击事件,这种响应不仅包括回调函数,还关系着点击时的动画效果。LVGL 一共提供了 27 个独立的标志,其中有 8 个可供用户自定义。可以检查 lv_obj_flag_t
枚举定义来查看包含的所有标志位。
开关
开关的创建
以上创建的通过点击来切换启用/关闭状态的按钮可以使用开关(switch)代替。创建开关和创建其它控件类似:
lv_obj_t* sw = lv_switch_create(lv_scr_act());
开关的效果如下,通过单击可以切换开关状态:
![2829890-20220614165911152-444557035.gif 2829890-20220614165911152-444557035.gif](data/attachment/forum/202312/10/203524wz01y51x1p1g5g6e.gif)
开关具有标志 LV_OBJ_FLAG_CHECKABLE
,因此可以响应事件 LV_EVENT_VALUE_CHANGED
。
开关的状态
一个控件可以具有多种标志,标志就是控件的抽象接口,决定了控件具有哪些行为。控件还具有多种不同的状态,在每种状态下,它的样式都是不一样的。可以通过
void lv_obj_add_state(lv_obj_t* obj, lv_state_t state);
给一个控件设置不同的状态来切换样式。例如,如果给开关设置状态 LV_STATE_CHECKED
,它会表现出打开的状态。不同状态下控件接收的响应也不一样,例如如果给开关加上 LV_STATE_DISABLED
的状态,点击时它就无法接收任何响应,连样式也不会再切换了。
可以在响应函数内通过 lv_obj_has_state(obj, state)
来判断一个控件处于什么状态,从而决定执行什么样的代码。这种方式更贴合控件的行为。
每个控件都有 9 种独立的状态,还有 4 种状态可以由用户自由定义,这些状态都被放在头文件 lv_obj.h
中。可以使用按位与运算符 |
给一个控件添加多个状态。例如,可以给一个开关设置为既开启又只读 LV_STATE_CHECKED | LV_STATE_DISABLED
,那么它的样式就会表现为:
![2829890-20220614165927743-2069142598.jpg 2829890-20220614165927743-2069142598.jpg](data/attachment/forum/202312/10/203536c4r83fs43a4rgf3s.jpg)
状态是在标志之上的概念,在不同的状态下控件可能具有不同的标志。
基本交互控件
下拉列表
下拉列表(drop-down list)也是一个非常简单的控件。下拉列表在点击后会出现一些选项,点击选择后就可以触发一些事件。
可以通过 lv_dropdown_set_options()
为下拉列表创建列表项:
lv_obj_t* drop01 = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options(drop01, "STM32F1\n"
"STM32F4\n"
"STM32H7\n"
"STM8");
LVGL 会自动拆分多行本文的每一行并分别创建一个列表项。下拉列表默认的行为是展示第一个列表项,并通过用户选择来切换展示的列表项:
![2829890-20220614165937378-216921398.gif 2829890-20220614165937378-216921398.gif](data/attachment/forum/202312/10/203547tqyqhqks6kv6rxel.gif)
下拉列表在选择列表项时会触发 LV_EVENT_VALUE_CHANGED
事件,可以通过
uint16_t lv_dropdown_get_selected(const lv_obj_t* obj);
void lv_dropdown_get_selected_str(const lv_obj_t* obj, char* buf, uint32_t buf_size);
来获取当前选中列表项索引或文本,如果要获取文本的话需要自行准备一个文本缓冲区。
下拉列表可以通过
void lv_dropdown_set_text(lv_obj_t* obj, const char* txt)
给它设置一个固定的文本,这样的下拉列表可以充当下拉菜单使用。
下拉列表还可以通过
void lv_dropdown_set_dir(lv_obj_t* obj, lv_dir_t dir);
void lv_dropdown_set_symbol(lv_obj_t* obj, const void* symbol);
修改列表项出现的位置和下拉列表右侧的符号,由此可以组合出上拉列表、左拉列表等。
滚动列表
滚动列表(roller)和下拉列表类似,不过它是通过滚动来切换选择的列表项的。
滚动列表的创建、事件响应和获取选中值的方式都和下拉列表类似。以下是滚动列表的创建方式:
lv_obj_t* roller01 = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller01,
"Monday\nTuesday\nWednesday\n"
"Thursday\nFriday\nSaturday\nSunday",
LV_ROLLER_MODE_INFINITE);
![2829890-20220614165948752-790765911.gif 2829890-20220614165948752-790765911.gif](data/attachment/forum/202312/10/203630cmd3eu1hfhh4x6vw.gif)
在设置列表项时滚动列表多了一个参数,代表滚动到底后需要停止还是循环往复。滚动列表非常适合用于列表项稍微有些多,没有足够的空间展示所有列表项的情况。因此,滚动列表还可以使用函数
void lv_roller_set_visible_row_count(lv_obj_t *obj, uint8_t row_cnt);
设置可见的列表项个数。如果设置为偶数,那么会有两个列表项只显示一半,就像动图中展示的一样。
转自:http://frozencandles.fun/archives/316
参考资料/延伸阅读
https://docs.lvgl.io/master/widgets/index.html
LVGL 官方文档——控件。在此可以查看更多文中没有提到的控件类型和使用细节,并查看官方编写的示例代码。
免责声明:
本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系进行删除。
原文作者:冰封残烛 原文地址:https://frozencandles.fun/archives/316