概念:
Lvgl虽然是以C语言进行编程开发,但其中借鉴了CSS当中编程思想,引入了类与对象的概念,其中lvgl的基础单位为控件,等同于HTML5当中的标签。
Lvgl当中的"类"是以结构体来进行实现的,以8.3版本为例(以下源码均为8.3):
通过lv_obj_t* obj =lv_xxx_create(parent)的形式进行实例化对象,lv_obj_t结构体来进行设置与保存控件的所有属性与样式,源码如下:
又以基础对象为基础模板,通过"类"的结构体衍生出其他控件,使得控件多样化,如:滑块、弧、按钮、键盘等。
这种操作有些类似于JS当中的构造函数,介于此,我们也就可以通过LVGL当中的内置函数来进行区分不同类型的控件了,lv_obj_check_type(obj, &lv_btn_class),该函数参数一为要检测的控件,参数二则是对应的类型,内部源码实现非常简单,仍然是使用的是lv_obj_t来进行储存相关信息,这都是典型的类与对象思维,源码如下:
结构:
lvgl对象结构依然与css结构相同,是以家庭结构来进行整体控制(也可以理解为套娃),最外层是整个屏幕,也可以理解为所有控件的祖先,当创建控件时,是以父子结构来进行创建,如果我们想在屏幕上创建一个按钮,则可以lv_btn_create(屏幕)。
其中屏幕则是按钮的"父亲",按钮与屏幕之间则为父子关系,如果我们又在按钮当中创建一段文字来表示按钮功能,则可以lv_label_create(按钮)。
其中关系为,屏幕是按钮的父亲,按钮是文字的父亲,屏幕则是文字的祖父,依赖于这种家庭制的关系,我们可以随时找到任意一个家庭成员,在lv_obj_t的结构体中保存了其父亲与子辈,注意:控件只有一个父亲,但可以有无数个儿子。
当然我们不可能去手动调动结构体来进行获取子控件或者父控件,那太不符合一个成熟的框架架构,LVGL提供了内置函数来进行获取其父控件与子控件,lv_obj_get_child(parent, id)与lv_obj_get_parent(Obj),其中lv_obj_get_child来进行获得子控件,参数分别是父控件与子控件的索引,这个索引通常是子控件创建的顺序(从0开始),lv_obj_get_parent则是用来获取控件的父控件。我们也可以通过源码来查看,他具体是怎么处理父子控件的,如果我们学过数据结构,则很容易看的懂其源码,在lv_obj_create中lv_obj_class_create_obj函数中有段源码如下:
但有时候,我们可能将某个或者多个子控件删除了,或者进行了其他操作,导致索引值不在等于创建顺序了,而且恰好子控件都是局部变量,那我们应该如何获取我们想要的子控件呢?
我们可以使用lv_obj_check_type,但这并不准确,如果我们创建很多个文本,我们很难用lv_obj_check_type来辨别出谁是我们想要的子控件。这时我们可能用的lvgl提供的另一个内置函数,lv_obj_set_user_data,这个函数可以将一个void*的数值绑定在控件时,当我获取到拿到子控件后,再通过lv_obj_get_user_data将其获取出来,然后再进行比对。
这个数据仍然是绑定在lv_obj_t的结构体当中,我们可以点开源码,看到他具体的位置与实现方式:
以上操作我们可能需要遍历所有子控件,那我们该如何获取子控件的数量呢?直接结构体调出来?通过结构体获取太长了导致不太好看,也就是可读性差,lvgl提供了封装好的函数接口,lv_obj_get_child_cnt,虽然也是调用结构体,但好看多了,源码如下:
有时候我们可能需要刷新列表内容,这时候可能需要清除所有内容,重新创建,有没有类似功能的函数接口,有的,lv_obj_clean(obj),调用该函数则会清空所有子控件。
至于内部如何实现的删除某个控件的,则是先遍历其所有子控件,递归删掉所有后代控件,判断是否在屏幕上显示,如果显示了则通知屏幕刷新,然后在其父控件的children中将其删除,感兴趣的可以去看源码。
言归正传这种家庭制与现实中的家庭及其相像,在现实中儿子一般都会遗传父母的长相,在LVGL中子控件则可以遗传父控件部分样式,称之为继承,就如同现实中,父亲长得很帅,儿子一般长得也不差。当然这并不意味着所有的外貌都可以遗传,比如父亲很胖,儿子却不一定很胖,在LVGL当中同样如此,子控件只能继承父控件的部分样式,以下为可以继承的样式:
1.文本相关属性
子控件若未显式设置以下属性,会直接继承父控件的值:
字体(text_font)
文本颜色(text_color)
文本透明度(text_opa)
文本对齐方式(text_align)
文本装饰(如下划线、删除线等)
示例:若父控件设置 lv_style_set_text_color(&parent_style, LV_COLOR_RED),子控件的文本默认也会显示为红色。
2.部分视觉属性
背景透明度(bg_opa)
部分渐变色属性(若父控件定义了全局渐变规则)
阴影颜色(shadow_color,但需父控件未覆盖子控件的显式设置)
注:这些属性的继承需符合特定条件,例如子控件未单独定义相关属性。
3.状态关联的样式
若父控件为特定状态(如 LV_STATE_PRESSED)定义了样式,子控件在相同状态下可能继承该状态的属性,除非子控件单独设置。
儿子除了可以继承父母的带来的便利以外,也会受到父母的影响,在LVGL中亦是如此,如果我们将父控件删除lv_obj_del(父亲),那么当父控件删除的那一刻,子控件也随之被删除。
在古代,我们有一种关系叫做过继,尤其是亲兄弟之间,如果有一方因为种种原因后继无人,无人继承家业,这时候如果亲兄弟有几个孩子,则可以过继一个来继承家业,lvgl也有同样的操作,lv_obj_set_parent该函数可以将控件换个父亲。
除了父子关系,还有兄弟关系,兄弟之间关联相较于父子关系则没有那么明显,主要是通过父亲来进行联系,但是兄弟之间也是有互相影响的,小儿子与小女儿往往会更受到父亲的喜爱,在LVGL中同样如此,如果两个控件共有一个父控件,并且位置重合的情况下,则会显示新建立的控件,情况如下图:
显示在其他控件上面的情况我们称之为前景,显示在下面的情况称之为背景。
但很多时候我们并不希望这种情况的发生,lvgl提供了相应的函数接口来控制谁显示在上面,谁显示在下面,当发生类似的情况时,我们调用这接口来进行控制。
使用lv_obj_move_foreground(obj) 显式地告诉库将对象带到前景。类似地,使用 lv_obj_move_background(obj) 将对象 obj 移动到背景。
也可以使用lv_obj_set_parent(obj,new_parent) , 将obj显示在 new_parent 的前面。
世子之争向来激烈,有时候老二不想当老二了,想当老大,有没有类似的操作,有的,lv_obj_move_to_index(obj,index),该函数主要作用是将控件移到指定索引,可能会问,谁在前谁在后,又不分家产,我还可以设置前景与后景,这有何用?有的!在布局当中会有大用。
而且前景与背景的设置也是调用的该函数,源码如下: