)
LVGL学习笔记三对象核心概念前言之前的两篇笔记中进行的示例都是在LVGL提供的官方示例中进行的但对于实际工程而言我们还是需要设计可视界面而在LVGL中对象是最核心的概念它是构建整个可视界面的基本单元。简单来说可以将对象理解为游戏《我的世界》中的“积木块”所有的界面元素都是由一个个对象组成的。故这篇笔记开始主要围绕LVGL中的对象相关的知识点展开介绍对象的属性和样式等概念最后举一个示例来演示如何创建一个对象。什么是对象LVGL 图形库虽然是由 C 语言开发的但其所采用的是一种面对对象编程思维。所谓面向对象就是把现实中的事物都抽象为“对象”。每个对象是唯一的且都可以拥有它的属性与行为。在LVGL中,无论是按钮、标签、文本框等基础控件还是自主设计的复杂界面组件本质上都是对象。它们都有各自不同的属性和行为调用不同的属性就能实现界面的不同功能。关于不同控件的不同点会在后续的学习笔记中提到。简单来说把对象想象成一块积木块一个按钮是一块积木一行文字是一块积木甚至整个屏幕也是一块积木。对象之间有什么关系如何理解父对象和子对象LVGL 采用父子结构管理对象。创建对象时候首先要创建一个父对象这个父对象一般有两种分别是使用对象A成为父对象。那边创建的对象B将会是对象A的子对象。使用屏幕作为父对象。在 LVGL中屏幕是没有父对象的特殊对象在默认的情况下LVGL 初始化时系统已经帮用户创建一个活动屏幕不需要我们再次创建。每个对象必须有一个父对象屏幕除外而父对象可以包含多个子对象。子对象的位置相对于父对象父对象移动时子对象会随之移动。简单来说你可以把一块积木放到另一块积木上面放上去的那块就是子被放的那块就是父。假设我们的屏幕是一张“桌子”那边父子积木的移动位置规律如下**子积木的位置是相对于父积木来算的。**假设将父积木放置在桌子的左上角然后设置子积木的位置坐标为1010那么子积木将会出现在父积木左上角往右往下各偏移10个像素的位置。父积木一动所有子积木跟着动。假设从桌子上将左上角的积木搬到右下角不用移动子积木子积木会自动移动右下角的父积木左上角往右往下各偏移10个像素的位置。父积木被删掉子积木也自动消失。我们把父积木从桌子上收到盒子里子积木不用收也会从桌子上消失。子积木超出父积木边界的部分会被切掉。如果子积木的体积大父积木的体积小由于父积木放不下这么大的子积木超过父积木的子积木部分会被切除。LVGL中的对象模型是什么所有UI元素都是对象(Object)对象具有层级关系(父子关系)对象继承父对象的属性对象可以接收事件并作出响应对象有自己的样式和状态所谓的对象模型承接上面的例子就是所有界面元素都是积木对象积木之间有父子嵌套关系所有积木共享一套通用的属性和操作方式同时各自保留自己的特殊功能。只需要像搭积木一样创建积木、设置样式、绑定事件、嵌套组合就能搭出完整的界面。属性、事件这些概念会在后面的笔记中提及这里不展开表述。使用LVGL对象模型有如下的优点规则统一布局方便内存安全C语言是面向过程的语言在LVGL中如何使用面向对象思想创建对象正如上面所讨论的LVGL图形库是用C语言写的使用到了“面向对象思想”但C语言本身没有类和继承这些概念故LVGL通过结构体的形式实现了“类”的功能并非真的“类”具体的实现逻辑如下图所示由上图可知在 LVGL 中首先定义了 lv_obj_t 这个结构体然后通过这个结构体去实例化一个基础对象lv_obj这个基础对象将作为父对象去衍生更多的子对象其他部件。沿用上面的那个比喻就是这些“积木”长得不同、功能不同但它们有一些共同的特点都有位置放在哪、大小多大多小、颜色、能不能被点击等等。然后通过这种“类”的方式去衍生其他部件所衍生出来的部件将会继承父对象的一些基本属性例如大小、位置、样式等等因此我们可以通过一套统一的函数去管理不同部件的基本属性。下面介绍一下创建对象中使用到的函数理解了这些函数的用法就可以创建我们这次的hello world示例了。lv_obj_t结构体的原型如下typedefstruct_lv_obj_t{constlv_obj_class_t*class_p;// 指向对象类的指针定义对象的行为和属性struct_lv_obj_t*parent;// 父对象指针表示对象的层级关系_lv_obj_spec_attr_t*spec_attr;// 特殊属性指针存储子控件、滚动条等信息_lv_obj_style_t*styles;// 样式指针定义对象的外观#ifLV_USE_USER_DATAvoid*user_data;// 用户自定义数据可选#endiflv_area_tcoords;// 对象的坐标区域lv_obj_flag_tflags;// 对象的标志位控制对象行为如可见性、可点击性等lv_state_tstate;// 对象的状态如默认、按下、禁用等uint16_tlayout_inv:1;// 布局无效标志uint16_tscr_layout_inv:1;// 屏幕布局无效标志uint16_tskip_trans:1;// 跳过过渡动画标志uint16_tstyle_cnt:6;// 样式数量uint16_th_layout:1;// 水平布局标志uint16_tw_layout:1;// 垂直布局标志}lv_obj_t;lv_label_create():创建标签对象LVGL 每个对象的创建函数都很类似它们具有高度统一的风格源码如下所示lv_obj_t*lv_widget_create(lv_obj_t*parent);在上述源码中widget 代表的是不同的部件例如开关switch、按钮btn等。一般情况下创建对象的函数只有一个形参那就是*parent它指向父类该父类可以是当前的活动屏幕lv_scr_act或者是其他的部件。这里使用的是函数lv_label_create()作用是创建一个用于显示文本信息的基本控件在示例中用来显示文字hello world。lv_scr_act():获取当前活动的屏幕调用函数lv_scr_act(),可以获取当前活动的屏幕其将返回指向活动屏幕的指针。源码如下所示staticinlinelv_obj_t*lv_scr_act(void){returnlv_disp_get_scr_act(lv_disp_get_default());}可以看到函数lv_scr_act()实际调用的是函数lv_disp_get_scr_act()其原型如下lv_obj_t*lv_disp_get_scr_act(lv_disp_t*disp){if(!disp)displv_disp_get_default();if(!disp){LV_LOG_WARN(no display registered to get its active screen);returnNULL;}returndisp-act_scr;}其中形参disp是指向LVGL显示设备驱动的指针返回值是指向当前活动屏幕对象的指针。lv_label_set_text():设置动态文本(字符串形式)其原型如下voidlv_label_set_text(lv_obj_t*label,constchar*text);形参label:为标签对象array是新的文本内容函数无返回值。lv_obj_center()对象的对齐这个函数是用于将指定对象在其父对象或屏幕中居中显示。其函数原型如下voidlv_obj_center(lv_obj_t*obj);参数obj指向需要居中的对象的指针,关于对象的大小和位置相关的内容会在下一篇笔记中重点提到这里只要会使用即可。了解完上述的函数我们可以开始创建我们自己的界面了这次的示例是屏蔽掉LVGL官方示例代码创建一个有hello world的界面具体代码如下intmain(){lv_init();if(!lv_win32_init(GetModuleHandleW(NULL),SW_SHOW,800,480,LoadIconW(GetModuleHandleW(NULL),MAKEINTRESOURCE(IDI_LVGL)))){return-1;}lv_win32_add_all_input_devices_to_group(NULL);lv_mainstart();/* 测试的demo */// ----------------------------------// Demos from lv_examples// ----------------------------------// lv_demo_widgets(); // 屏蔽官方示例代码// lv_demo_benchmark();// ----------------------------------// Task handler loop// ----------------------------------while(!lv_win32_quit_signal){lv_task_handler();Sleep(1);}return0;}voidlv_mainstart(void){lv_obj_t*scrlv_scr_act();//获取当前活动屏幕默认只有一个屏幕lv_obj_t*labellv_label_create(scr);//创建一个标签Label对象父对象是屏幕lv_label_set_text(label,Hello World!);//设置标签的文本lv_obj_center(label);//将标签在屏幕上居中}测试结果如下