目录
1、基础对象基本属性用法(所有部件都可以用)
1.1、基础对象的大小(Size)
1.2、基础对象的位置(Position)
1.3、基础对象对齐(Alignment)
1. 4、基础对象的盒子模型(border-box)
1.5、基础对象的样式(styles)
1.6、基础对象的事件(events)
2、标签(lv_label)
2.1、显示图标
2.2、显示中文
3、按钮(lv_btn)
4、使用物理按键代替触摸(groups)
LVGL采用面向对象的编程思想(OOP),她的基本构造块(类)是对象(实例),也就是我们所说的部件(Widgets)就是一个个部件,比如button、label、image等等。
lvgl编程思想:lv_obj_t (类)定义了部件的抽象特点,其定义包含了数据的形式以及对数据的操作。部件(子类)比原本的类(称为父类或基类)要更加具体化,子类会继承父类的属性和行为。
在LVGL中,所有的对象都在 lv_obj_t 这个结构体的基础上进行演变,所以我们就看到了各种不一样的部件,就算是一样的部件,继承基础父类(基类)之后演变出来对象(实例)的形态或风格样式都不一样,如下图举例一下。
屏幕是没有父类的基础对象
lv_obj_create() 这个函数是让我们所选的部件实例化,实例化之后就可以对其进行操作。
其参数填写创建在谁之上,可以在其他部件上也可以在我们屏幕上。屏幕分为三类:
1、lv_scr_act(void); // 活动屏幕
2、lv_layer_top (void); // 顶层
3、lv_layer_sys (void); // 系统层
设置宽度: lv_obj_set_width(obj, new_width);
设置高度: lv_obj_set_height(obj, new_height);
同时设置宽度、高度: lv_obj_set_size(obj, new_ width, new_ height);
获取宽度: lv_obj_get_width(obj);
获取高度: lv_obj_get_height(obj);
LVGL的坐标系是我们 可以称为“LCD坐标系”
如下是屏幕的位置坐标:(x和y根据自己需求进行调整)
设置x轴方向的坐标位置: lv_obj_set_x(obj, new_x);
设置y轴方向的坐标位置: lv_obj_set_y(obj, new_y);
同时设置x、y坐标位置: lv_obj_set_pos(obj, new_x, new_y); // position
获取x轴坐标位置: lv_obj_get_x(obj);
获取y轴坐标位置: lv_obj_get_y(obj);
参照父对象对齐: lv_obj_set_align(obj, LV_ALIGN_...);
参照父对象对齐后再设置坐标位置: lv_obj_align(obj, LV_ALIGN_..., x, y);
参照另一个对象(无父子关系)对齐后设置坐标位置: lv_obj_align_to(obj_to_align, obj_referece, LV_ALIGN_..., x, y)
LVGL 遵循 CSS 的 border-box 模型。 对象的“盒子”由以下部分构成:
边界(bounding):元素的宽度/高度围起来的区域(整个盒子)。
边框(border):边框有大小和颜色等属性(相当于盒子的厚度和它的颜色)。
填充(padding):对象两侧与其子对象之间的空间(盒子的填充物)。
内容(content):如果边界框按边框宽度和填充的大小缩小,则显示其大小的内容区域(盒子实际装东西的区域)。
轮廓(outline) :LVGL中没有外边距(margin)的概念(盒子之间的距离),确认代之的是轮廓(outline)。它是绘制于元素(盒子)周围的一条线,它不占据空间,位于边框边缘的外围,可起到突出元素(盒子)的作用。在浏览器里,当鼠标点击或使用Tab键让一个选项或者一个图片获得焦点的时候,这个元素就会多了一个轮廓框围绕。轮廓(outline)
下面是对CSS盒子模型的一些扩充(左是框图,右是例子)
网页设计中常听的属性名:内容(content)、内边距(padding)、边框(border)、外边距(margin), CSS盒子模式都具备这些属性。这些属性我们可以把它转移到我们日常生活中的盒子(箱子)上来理解,日常生活中所见的盒子也就是能装东西的一种箱子,也具有这些属性,所以叫它盒子模型。CSS盒子模型就是在网页设计中经常用到的CSS技术所使用的一种思维模型。
content就是盒子里装的东西,它有高度(height)和宽度(width),可以是图片,可以是文字或者小盒子嵌套,在现实中,内容不能大于盒子,内容大于盒子就会撑破盒子,在LVGL中盒子不会变化,会产生滚动条,我们可以滚动查看超出盒子的内容;但在css中,盒子有弹性的,顶多内容太大就会撑大盒子,但是不会损害盒子。
padding即是填充,就好像我们为了保证盒子里的东西不损坏,填充了一些东西,比如泡沫或者塑料薄膜,填充物有大有小,有软有硬,反应在网页中就是padding的大小了。
border就是再外一层的边框,因为边框有大小和颜色的属性,相当于盒子的厚度和它的颜色或者材料。
margin外边距,就是我们的盒子与其他的盒子或者其他东西的距离。假如有很多盒子,margin就是盒子之间直接的距离,可以通风,也美观同时方便取出。
Styles 用于设置对象的外观
- 样式是一个 lv_style_t 变量,它可以保存边框宽度、文本颜色等属性。
- 将样式(变量)分配给对象就可以改变其外观。在赋值过程中,可以指定目标部分和目标状态。
- 一个样式可以给多个对象使用(正常样式)。
- 样式可以级联,也就是可以将多个样式分配给一个对象。所以,我们不用将所有属性都在一个样式中指定,可以通过多个样式组合的形式指定。 LVGL 会优先使用我们定义的样式,如果没有就会使用默认值。
- 后来添加的样式具有更高的优先级。也就是说如果在两种样式中指定了同一个属性,则将使用最后添加的样式。
- 如果对象中未指定某些属性(例如文本颜色),就会从父级继承。
- 上面说的是 “正常” 样式,对象还有本地样式,它比 “正常” 样式具有更高的优先级。
- 可以定义有过渡效果的样式。
- 默认有一个样式主题,我们也可以自己定义样式主题,作为默认的样式主题使用。
样式操作流程:
static lv_style_t style_obj; 样式变量应该是 静态 、全局或动态分配的
lv_style_init(&style_obj); 初始化
lv_style_set_
(&style, ); 设置样式属性(很多) lv_obj_add_style(obj, &style,
) 添加(应用)样式到对象
lv_obj_add_style(obj, &style_obj, 0); // 默认(常用)
lv_obj_add_style(obj, &style_obj, LV_STATE_PRESSED); // 在对象被按下时应用样式
获取样式属性
lv_obj_get_style_
(obj, ); lv_color_t color = lv_obj_get_style_bg_color(obj, LV_PART_MAIN); 返回默认值
删除样式
lv_obj_remove_style_all(obj); 删除对象的所有样式:
lv_obj_remove_style(obj, &style_obj, selector); 删除对象的特定样式:
如果 selector 是 LV_STATE_ANY 或 LV_PART_ANY 就会删除具有任何状态或部分的样式。
(如删除对象所有样式一样)
背景属性和我们前面学习的盒子模型关系很大,背景属性主要有一下这些:
- 背景(Background)
- 边界(Border)
- 轮廓(Outline)
- 阴影(Shadow)
- 填充(Padding)
- 宽度和高度变换 X和Y变换
状态(States)对象可以处于以下状态的组合:
LV_STATE_DEFAULT (0x0000) 正常,释放状态
LV_STATE_CHECKED (0x0001) 切换或检查状态
LV_STATE_FOCUSED (0x0002) 通过键盘或编码器聚焦或通过触摸板/鼠标点击 LV_STATE_FOCUS_KEY (0x0004) 通过键盘或编码器聚焦,但不通过触摸板/鼠标聚焦 LV_STATE_EDITED (0x0008) 由编码器编辑
LV_STATE_HOVERED (0x0010) 鼠标悬停(现在不支持)
LV_STATE_PRESSED (0x0020) 被按下
LV_STATE_SCROLLED (0x0040) 正在滚动
LV_STATE_DISABLED (0x0080) 禁用状态
LV_STATE_USER_1 (0x1000) 自定义状态
LV_STATE_USER_2 (0x2000) 自定义状态
LV_STATE_USER_3 (0x4000) 自定义状态
LV_STATE_USER_4 (0x8000) 自定义状态
对象可以有 部分(parts) ,它们也可以有自己的样式。LVGL 中存在以下预定义部分:
LV_PART_MAIN 类似矩形的背景
LV_PART_SCROLLBAR 滚动条
LV_PART_INDICATOR 指标,例如用于滑块、条、开关或复选框的勾选框 LV_PART_KNOB 像手柄一样可以抓取调整值
LV_PART_SELECTED 表示当前选择的选项或部分
LV_PART_ITEMS 如果小部件具有多个相似元素(例如表格单元格)
LV_PART_TICKS 刻度上的刻度,例如对于图表或仪表
LV_PART_CURSOR 标记一个特定的地方,例如文本区域或图表的光标 LV_PART_CUSTOM_FIRST 可以从这里添加自定义部件
除了“普通” 样式外,对象还可以存储 本地样式(私有样式)
本地样式与普通样式类似,但是它不能在其他对象之间共享
使用本地样式,将自动分配局部样式,并在删除对象时释放
本地样式的接口函数是这样的格式:
lv_obj_set_style_
(obj, , ); 删除本地样式的时候我们删除某一个样式:
lv_obj_remove_local_style_prop(obj, LV_STYLE_..., selector);
当发生用户可能感兴趣的事情时,LVGL 中会触发事件。
添加事件:
lv_obj_add_event_cb(obj, event_cb, event_code, user_data);
发送事件:
lv_event_send(obj, event_cb, event_code, user_data);
删除事件:
lv_obj_remove_event_cb(obj, event_cb);
lv_obj_remove_event_dsc(obj, event_dsc);//event_dsc 是 lv_obj_add_event_cb 返回的指针
事件类型(event_code)
- 输入设备事件(Input device events)
- 绘图事件(Drawing events)
- 其他事件(Special events)
- 特殊事件(Other events)
- 自定义事件(Custom events)
事件回调函数的 lv_event_t 参数
static void my_event_cb(lv_event_t * event);
获取触发的事件代码: lv_event_code_t code = lv_event_get_code(e);
获取触发事件的对象: lv_obj_t * target = lv_event_get_target(e);
获取最初触发事件的对象(事件冒泡): lv_obj_t * target = lv_event_get_current_target(e); 获取事件传递的用户数据:
lv_event_get_user_data(e); 获取使用 lv_obj_add_event_cb 传递的用户数据
lv_event_get_param(e); 获取使用 lv_event_send 传递的用户数据
- 一个事件回调函数可给多个对象使用
- 一个对象可以使用多个事件回调函数
- 如果传入的用户数据不一样,一个对象可以绑定同一个事件回调函数多次,事件将按照添加的顺序调用
事件冒泡
如果对象启用了 lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE),该对象的所有事件将会发送到该对象的父级。如果父级也启用了 LV_OBJ_FLAG_EVENT_BUBBLE,那么事件继续发送到他的父级,依此类推。
lv_event_get_target(e); 获取触发事件的当前对象。
lv_event_get_current_target(e); 获取事件冒泡的父对象。
标签是用来显示文本的基本对象类型
标签的组成包括:
LV_PART_MAIN 矩形部分(盒子区域)。 填充值可用于在文本和背景之间添加空间。 LV_PART_SCROLLBAR 当要展示的文本大于部件的大小时,显示的滚动条部分。 LV_PART_SELECTED 选中文本时,突出显示的部分。label只能使用 text_color 和 bg_color 样式属性。
创建标签: lv_obj_t * label = lv_label_create(parent);
直接设置要显示的文本: lv_label_set_text(label, "New text");
格式化给定要显示的文本: lv_label_set_text_fmt(label, “%s: %d”, “Value”, 15);
文本不存储在动态内存中,而是直接使用给定的缓冲区:
lv_label_set_text_static(label, "New text");
要在label换行非常简单,像printf函数那样使用 \n 即可:
lv_label_set_text(label, " line1\nline2\n\nline4 ");
如果出现文本的宽度或高度大于或小于label的情况,就需要做一些调整,下面是几种模式
- LV_LABEL_LONG_WRAP 如果有多个换行,并且如果高度为LV_SIZE_CONTENT,那么高度会根据文本换行被自动扩展;否则文本将被剪掉。(默认设置)
- LV_LABEL_LONG_DOT 如果文本太长,就保持大小并在末尾写3个点 .
- LV_LABEL_LONG_SCROLL 如果文本比标签宽(太长),则可以水平来回滚动显示它。如果它很高(多个\n换行),可以垂直滚动。只滚动一个方向,水平滚动的优先级更高。
- LV_LABEL_LONG_SCROLL_CIRCULAR 如果文本比标签宽,则水平滚动它。如果它更高,就垂直滚动。只滚动一个方向,水平滚动的优先级更高。
- LV_LABEL_LONG_CLIP 剪掉超出标签范围外的文本部分。
lv_label_set_long_mode(label, LV_LABEL_LONG_...) 指定模式
注意:
LV_LABEL_LONG_DOT 是直接操作文本缓冲区以添加/删除点。如果使用 lv_label_set_text 和 lv_label_set_text_fmt 它们会分配一个单独的缓冲区,不会出问题。
但是如果使用 lv_label_set_text_static 时我们传递给它的缓冲区必须是可写的。
文本重新着色,可以通过样式来上色
lv_style_set_text_color(&style_obj, lv_color_hex(0xf7b37b)); //样式着色 lv_obj_set_style_text_color(label, lv_color_hex(0xf7b37b), 0); //样式着色
lv_label_set_recolor(label1, true); //label从新着色
lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label);
如果在 lv_conf.h 中打开了 LV_LABEL_TEXT_SELECTION (默认开启),就可以选择部分文本。这个效果只能在文本框(lv_textarea)中实现。Label只能事先手动选择指定范围的文本:
lv_label_set_text_sel_start(label, 1); lv_label_set_text_sel_end(label, 6);
注意,这里的第一个字符从1开始算,而不是0。
LVGL内置了一些图标,它们是全局变量我们可以直接使用:
用法:
lv_label_set_text(my_label, LV_SYMBOL_OK); // 直接显示图标 lv_label_set_text(my_label, LV_SYMBOL_OK “Apply”); // 图标与字符串一起使用 lv_label_set_text(my_label, LV_SYMBOL_OK LV_SYMBOL_WIFI LV_SYMBOL_PLAY);
Label默认不接收输入事件,如果我们想设置输入类型的样式或者事件会无法生效,就需要打开 LV_OBJ_FLAG_CLICKABLE,示例:
lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE); // 使输入设备可点击对象
LVGL有一个中文字库 CJK字库 ,字库在 lv_conf.h中定义为:LV_FONT_SIMSUN_16_CJK。
要在lvgl中使用显示自己的中文字库,我们需要用到两个东西:字体文件和字体转换器。 字体文件我们可以使用开源的字体或者自己制作出来,准备好了字体文件之后使用字体转换器即可转换成可以在lvgl上使用的字体格式。
开源字体获取页面: http://lvgl.100ask.net/8.1/tools/fonts-zh-source.html
字体转换工具界面:
http://lvgl.100ask.net/8.1/tools/fonts-zh-source.html
https://lvgl.io/tools/fontconverter
2.2.1、如何使用字体转换器?
- 为要输出字体命名。例如“font_source_han_sans_bold_20”
- 以 px为单位指定高度(字体大小)
- 设置bpp (bit-per-piel)。值越高,字体越平滑(抗锯齿)
- 选择TTF 或 WOFF 格式字体文件
- 设置要包含在字体中的 Unicode 字符范围或在符号字段中列出字符
- 可以同时选择多个字体文件转换,并为其指定范围和/或符号。这些字符将被合并转换到同一个文件中。
- 单击转换按钮以下载转换出来的 font_source_han_sans_bold_20.c 文件。
2.2.2如何在 LVGL 中使用生成的字体?
- 将结果 C 文件复制到你的 LVGL 项目中,并包含到项目;
- 在你的项目应用程序的 C 文件中,将字体声明为: extern lv_font_t my_font_name; 或: LV_FONT_DECLARE(my_font_name);
- 在样式中设置字体: lv_style_set_text_font(&style_obj, &my_font_name); // 普通(共享)样式 lv_obj_set_style_text_font(label, &my_font_name, 0); // 私有(本地)样式
- unicode 是统一所有语言的一套编码。
- utf-8是基于unicode编码的一种节约字节的编码。
按钮是和基础对象最像的部件。与基础对象相比,按钮没有新的功能
按钮和基础对象的不同点:
不可滚动
添加到默认组(可修改为给其他输入设备控制用,比如按键)
高度和宽度默认为 LV_SIZE_CONTENT
创建按键的方法: lv_obj_t * btn = lv_btn_create(parent);
按钮的部分(Part)只有下面这个: LV_PART_MAIN 按钮的背景
样式修改示例:
lv_obj_set_style_
(obj, , ); // 本地(私有)样式 lv_obj_add_style(obj, &style, ) // 普通(共享)样式 所以修改样式的时候我们修改部分(Part)的时候只有 LV_PART_MAIN 可以生效;状态部分和前面说的样式状态(States)都可以用
如果打开了 LV_OBJ_FLAG_CHECKABLE ,当对象被点击时有选中切换(Toggle)状态的效果,并且可以在 LV_EVENT_VALUE_CHANGED 事件类型中处理事件,比如: lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
总的来看,按钮是用来按的(单击、按下或不按下),所以我们用物理按键控制的时候只需要LV_KEY_ENTER 就可以控制按钮, 并且 LV_KEY_ENTER 可以触发LV_EVENT_PRESSED/PRESSING/RELEASED 等事件类型。
在LVGL中我们可以使用键盘或编码器替换触摸板或鼠标控制。它的工作原理类似于 PC 上的 TAB 键,用于选择应用程序或网页中的元素。
- 首先我们需要将要用键盘或编码器控制的部件添加到组,有的部件在创建时会加入到了默认组。
- 在每个组中,同时只有一个对象聚焦并接收按键或编码器动作。
- 将对象添加到组还不够,我们还需要将输入设备与组关联。一个输入设备只能将按键发送给一组,反过来一组可以从多个输入设备接收数据。
5种输入设备类型:
LV_INDEV_TYPE_NONE 不使用输入设备(未初始化状态) LV_INDEV_TYPE_POINTER 触摸板、鼠标、外接按钮
LV_INDEV_TYPE_KEYPAD 键盘
LV_INDEV_TYPE_BUTTON 分配给屏幕特定点的外部(硬件按钮) LV_INDEV_TYPE_ENCODER 只有左、右和按下三个按键的编码器(比如鼠标中间的滚轮)
自定义组创建过程:
- 首先要创建一个 组(Groups) : lv_group_t * g = lv_group_create();
- 然后将一个对象添加到 组(Groups) 中: lv_group_add_obj(g, obj);
- 最后要将组(Groups)与输入设备相关联: lv_indev_set_group(indev, g); 其中 indev 是 lv_indev_drv_register(); 的返回值
使用默认组
在lvgl中有些部件,在创建的时候加入到默认组中,但是默认组变量lvgl并没有帮我们初始化好,我们需要在创建部件之前初始化好才能使用默认组,这个自定义组一样不同的是对于创建时添加到默认的部件我们可以跳过lv_group_add_obj:
lv_group_t * g = lv_group_create(); // 创建一个组
lv_group_set_default(g); // 设置为默认组
lv_indev_set_group(indev, g); // 将组和输入设备相关联
有一些预定义的键具有特殊含义:
必须的按键
最重要且建议必要的按键是:
LV_KEY_NEXT/PREV
LV_KEY_ENTER
LV_KEY_UP/DOWN/LEFT/RIGHT
在我们回调函数 read_cb 中,应该优先考虑将现有的键对应转换为上面这些键,以便能在组中导航并与所选对象进行交互。
其实一般来说,只使用 LV_KEY_LEFT/RIGHT 就足够了,因为大多数对象都可以通过它们被完全控制。
对于编码器,默认只使用 LV_KEY_LEFT、LV_KEY_RIGHT 和 LV_KEY_ENTER
编辑(edit)和导航(navigate)模式
- 如果我们使用编码器,因为编码器的按键有限,所以在各个对象之间进行导航就不方便了。这个时候就需要区分模式了,lvgl帮我们区分了两种模式:编辑和导航模式
- 在导航模式下,编码器的 LV_KEY_LEFT/RIGHT 被转换为 LV_KEY_NEXT/PREV,这样就可以通过转动编码器来选择下一个或上一个对象。
- 如果需要修改对象的值,比如有个滑杆(lv_slider)代表音量或亮度,我们通过按下 LV_KEY_ENTER 将模式切换为编辑模式,这样就可以通过转动编码器修改滑杆的值。然后根据对象的类型,短按或长按 LV_KEY_ENTER 切换为导航模式(离开编辑模式)。
按键控制的初始化、工作流程和使用
其实还有许多部件,这里就不一一介绍了,很多部件原理都差不多,这里推荐一个学习LVGL非常不错的网址 量规(lv_gauge) — 百问网LVGL中文教程手册文档 1.0 文档
韦东山老师的视频真不错,底层去解剖程序,看一遍不懂就两遍。