lvgl入门学习第二篇lvgl基础对象
一、对象(lv_obj_t)
-
LVGL采用面向对象的编程思想(OOP),她的基本构造块(类)是对象(实例),也就是我们所说的部件(Widgets)就是一个个部件,比如button、label、image等等
-
lv_obj_t (类)定义了部件的抽象特点,其定义包含了数据的形式以及对数据的操作。部件(子类)比原本的类(称为父类或基类)要更加具体化,子类会继承父类的属性和行为。
-
在LVGL中,所有的对象都在 lv_obj_t 这个结构体的基础上进行演变,所以我们就看到了各种不一样的部件,就算是一样的部件,继承基础父类(基类)之后演变出来对象(实例)的形态或风格样式都不一样。
-
由于lvgl使用c语言编写,因此 lv_obj_t 只能通过结构体来表示,它并不是一个实例化后的类,因此我们需要先实例化出一个父类(lv_obj即基础对象)(基类),其他所有的部件(对象)都继承自这个父类(基类)。
具体逻辑关系如下图:
二、基础对象(lv_obj)
屏幕是没有父类的基础对象
2.1 屏幕对象的创建过程
lv_init
_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); // 初始化显示器链表
lv_disp_drv_register
_lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll)); // 注册显示器到链表
disp->act_scr = lv_obj_create(NULL); // 在显示器上创建一个默认屏幕
lv_obj_class_create_obj
obj->coords.x1 = 0;
obj->coords.y1 = 0;
obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1; // 设置屏幕的水平宽度
obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1; // 设置屏幕的垂直高度
2.2 LVGL的三层屏幕
lv_scr_act(void); // 活动屏幕 disp->act_scr
lv_layer_top (void); // 顶层 disp->top_layer
lv_layer_sys (void); // 系统层 disp->sys_layer
三、基础对象的大小(size)
3.1 设置大小
设置宽度:lv_obj_set_width(obj, new_width);
设置高度:lv_obj_set_height(obj, new_height);
同时设置宽度、高度:lv_obj_set_size(obj, new_ width, new_ height);
3.2 获取大小
获取宽度:lv_obj_get_width(obj);
获取高度:lv_obj_get_height(obj);
四、基础对象的位置(Position)
4.1 LVGL屏幕的原点
- “笛卡尔坐标系”,又叫“直角坐标系”,学习数学的时候一般用这种坐标系。
- LVGL屏幕的坐标系,又称为“LCD坐标系”,他的原点位置和直角坐标系不一样。
- 确定了坐标系类型就确定了原点,确定了原点就可以设置和确定LVGL部件(对象)的位置。
- LVGL的原点位置在屏幕左上角。
4.2 屏幕(部件区域)的表示
LVGL的原点位置在屏幕左上角
4.3 位置(Position)
4.3.1 设置位置(Set position)
设置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
4.3.2 获取位置(Get position)
获取x轴坐标位置:lv_obj_get_x(obj);
获取y轴坐标位置:lv_obj_get_y(obj);
4.4 对齐(Alignment)
参照父对象对齐: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)
4.4.1 对齐类型(LV_ALIGN_...)
五、基础对象的盒子模型(border-box)
对象的“盒子”由以下部分构成:
边界(bounding):元素的宽度/高度围起来的区域(整个盒子)。
边框(border):边框有大小和颜色等属性(相当于盒子的厚度和它的颜色)。
填充(padding):对象两侧与其子对象之间的空间(盒子的填充物)。
内容(content):如果边界框按边框宽度和填充的大小缩小,则显示其大小的内容区域(盒子实际装东西的区域)。
轮廓(outline) :LVGL中没有外边距(margin)的概念(盒子之间的距离),确认代之的是轮廓(outline)。它是绘制于元素(盒子)周围的一条线,它不占据空间,位于边框边缘的外围,可起到突出元素(盒子)的作用。 例如:在浏览器里,当鼠标点击或使用Tab键让一个选项或者一个图片获得焦点的时候,这个元素就会多了一个轮廓框围绕 。
LVGL的盒子模型是理解对象(部件)的组成,修改对象的样式,实现对对象的布局、处理对象排列等的关键。
六、基础对象的样式(styles)
Styles 用于设置对象的外观。
- 样式是一个 lv_style_t 变量,可以保存边框宽度、文本颜色等属性。
- 将样式(变量)分配给对象就可以改变其外观。在赋值过程中,可以指定目标部分和目标状态。
- 一个样式可以给多个对象使用(正常样式)。
- 样式可以级联,就是将多个样式分配给一个对象。所以,我们不用将所有属性都在一个样式中指定,可以通过多个样式组合的形式指定。 LVGL 会优先使用我们定义的样式,如果没有就会使用默认值。
- 后来添加的样式具有更高的优先级。就是说如果在两种样式中指定了同一个属性,则将使用最后添加的样式。
- 如果对象中未指定某些属性(例如文本颜色),就会从父级继承。
- 对象的本地样式比“正常”样式具有更高的优先级。
- 可以定义有过渡效果的样式。
- 默认有一个样式主题,可以自己定义样式主题,作为默认的样式主题使用。
6.1 初始化样式
样式存储在 lv_style_t 变量,样式变量应该是 静态 、全局或动态分配 的。不能是函数中的局部变量,因为当函数结束时它们会被销毁。样式初始化示例:
static lv_style_t style_obj;
lv_style_init(&style_obj);
6.2 设置样式属性
当初始化好一个样式后就可以设置它的样式属性,接口函数如下格式:
lv_style_set_<property_name>(&style, <value>);
示例:
lv_style_set_bg_color(&style_obj, lv_color_hex(0x000000)); // 设置背景色
lv_style_set_bg_opa(&style_obj, LV_OPA_50); // 设置背景透明度
lv_style_set_....
6.3 添加(应用)样式到对象
当初始化并且设置好一个样式后就可以将它添加到对象上面,接口函数只有一个:lv_obj_add_style(obj, &style, <selector>)
- 参数 “obj” 就是要添加到的对象
- “style” 是指向样式变量的指针
是应添加样式的部分和状态的 OR-ed 值 (不能是互斥,否则就是清除标志,没法合并)。
示例:
lv_obj_add_style(obj, &style_obj, 0); // 默认(常用)
lv_obj_add_style(obj, &style_obj, LV_STATE_PRESSED); // 在对象被按下时应用样式
6.4 获取样式属性
获取属性的最终值(考虑级联、继承、本地样式和转换),接口函数如下格式:lv_obj_get_style_<property_name>(obj, <part>);
函数使用对象的当前状态,如果没有更好的候选对象,则返回默认值。
例如:
lv_color_t color = lv_obj_get_style_bg_color(obj, LV_PART_MAIN);
6.5 删除样式
删除对象的所有样式:lv_obj_remove_style_all(obj);
删除对象的特定样式:lv_obj_remove_style(obj, &style_obj, selector);
// 1、只有当 selector 与 lv_obj_add_style 中使用的 selector 匹配时,此函数才会删除 style
// 2、如果 style 是空,那么会根据给出的 selector 检查并删除所有匹配的样式
// 3、如果 selector 是 LV_STATE_ANY 或 LV_PART_ANY 就会删除具有任何状态或部分的样式。
6.6 查看样式属性
所有的可用的样式属性可以在文档或者代码中获取得到。
文档位置:
- 英文原版:https://docs.lvgl.io/8.1/overview/style-props.html
- 中文翻译:http://lvgl.100ask.net/8.1/overview/style-props.html
代码位置:
- 普通样式:lvgl/src/misc/lv_style_gen.h
- 本地样式:lvgl/src/core/lv_obj_style_gen.h
文档位置和代码位置在后续的版本更新中会发生变化,只供参考。
6.7 背景部分的属性
背景属性和盒子模型关系很大,背景属性主要有一下这些:
- 背景(Background)
- 边界(Border)
- 轮廓(Outline)
- 阴影(Shadow)
- 填充(Padding)
- 宽度和高度变换
- X和Y变换
6.8 样式的状态和部分
对象可以处于以下状态的组合:
- 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 可以从这里添加自定义部件。
这些可能会随着lvgl的更新而不断增加,可以阅读最新版本的文档获取最新资料。
例如一个 滑杆(Slider) 包含三个部分:
- 背景
- 指标
- 旋钮
这意味着滑块的所有三个部分都可以有自己的样式。
示例体验:http://lvgl.100ask.net/8.1/widgets/core/slider.html#slider-with-custom-style
默认状态如下:
调整滑杆如下:
6.9 本地样式
对象还可以存储在 本地样式(私有样式)
。
- 本地样式不能在其他对象之间共享。
- 如果使用本地样式,将自动分配局部样式,并在删除对象时释放。
- 本地样式对于向对象添加本地自定义很有用。
本地样式的接口函数的格式:
lv_obj_set_style_<property_name>(obj, <value>, <selector>);
示例:
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffff), 0); // 设置背景色lv_obj_set_style_bg_opa(obj, LV_OPA_50, 0); // 设置背景透明度lv_style_set_style_....
删除本地样式的时候我们删除某一个样式:
lv_obj_remove_local_style_prop(obj, LV_STYLE_..., selector);
LV_STYLE_...的取值请看: lvgl/src/misc/lv_style.h 中的 lv_style_prop_t
6.10 样式继承
- 某些属性(通常与文本相关)可以从父对象的样式继承。
- 只有没有在为对象设置样式属性的时候,才应用继承。 在这种情况下,如果这个属性是可继承的,那这个属性的值会在父类中检索,直到一个对象为该属性指定了一个值。父类将使用自己的状态来确定该值。 因此,如果按下按钮,并且文本颜色来自此处,则将使用按下的文本颜色。
6.11 过渡特效
默认情况下,当一个对象改变状态(例如它被按下)时,新状态的新属性会立即设置。但是,通过转换,可以在状态更改时播放动画。 例如,按下按钮时,其背景颜色可以在 300 毫秒内动画显示为按下的颜色。
Demo体验:
https://docs.lvgl.io/8.1/overview/style.html#transition
http://lvgl.100ask.net/8.1/overview/style.html#transition
6.12 样式主题
主题是风格的集合。如果存在活动主题,LVGL将其应用于每个创建的部件(对象)。 这将为UI提供一个默认外观,然后可以通过添加更多样式对其进行修改。
Demo体验:
https://docs.lvgl.io/8.1/overview/style.html#extending-the-current-theme
http://lvgl.100ask.net/8.1/overview/style.html#extending-the-current-theme
七、基础对象的事件(events)
1、什么是事件??
当发生用户可能感兴趣的事情时,LVGL 中会触发事件
,例如当一个对象:
- 被点击
- 滚动
- 数值改变
- 重绘
- 等等。。。
2、事件的处理
添加事件: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 返回的指针
3、事件类型(event_code)[源码:lvgl/src/core/lv_event.h (lv_event_code_t)
]
- 输入设备事件(Input device events)
- 绘图事件(Drawing events)
- 其他事件(Special events)
- 特殊事件(Other events)
- 自定义事件(Custom events)
开发文档:
英文:https://docs.lvgl.io/8.1/overview/event.html#event-codes
中文:http://lvgl.100ask.net/8.1/overview/event.html#event-codes
4、事件回调函数的 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_obj_add_event_cb 传递的用户数据lv_event_get_param(e);
获取使用 lv_event_send 传递的用户数据
- 一个事件回调函数可给多个对象使用
- 一个对象可以使用多个事件回调函数
- 如果传入的用户数据不一样,一个对象可以绑定同一个事件回调函数多次,事件将按照添加的顺序调用。例如:
lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num1);
lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num2);
5、事件冒泡
如果对象启用了 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); 获取事件冒泡的父对象。
本文来自博客园,作者:登云上人间,转载请注明原文链接:https://www.cnblogs.com/lj15941314/p/17207680.html