LVGL 中使用 lv_obj_t 来对常用的对象进行抽象:
typedef struct _lv_obj_t {
const lv_obj_class_t * class_p;
struct _lv_obj_t * parent;
_lv_obj_spec_attr_t * spec_attr;
_lv_obj_style_t * styles;
#if LV_USE_USER_DATA
void * user_data;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
} lv_obj_t;
obj 结构作为 LVGL 内的一个基本抽象,包含了 area,style,parent 等内容;
以一个 button 为例,应用层代码创建一个 button 的代码;
创建对象
void LVGL_CentralButton(void)
{
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_height(btn, 30);
lv_obj_t *label;
label = lv_label_create(btn);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label, "LVGL");
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 10);
lv_style_set_border_color(&style_btn, lv_color_white());
lv_style_set_border_opa(&style_btn, LV_OPA_30);
lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT);
}
首先定义了一个 lv_obj_t *btn 的一个对象,然后调用 lv_btn_create 来创建一个 button;
#define MY_CLASS &lv_btn_class
const lv_obj_class_t lv_btn_class = {
.constructor_cb = lv_btn_constructor,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_btn_t),
.base_class = &lv_obj_class
};
lv_obj_t * lv_btn_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
在每个组件中(比如 button、label)都定义了一个 MY_CLASS 的玩意,而且仅该文件使用,充分体现了 LVGL 将组件抽象为 class 的思想;
这个MY_CLASS 是一个 lv_obj_class_t 结构;
每一个 lv_obj_class_t 结构都包含了他的构造函数和析构函数,默认长宽等信息,如下所示:
typedef struct _lv_obj_class_t {
const struct _lv_obj_class_t * base_class;
void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
#if LV_USE_USER_DATA
void * user_data;
#endif
void (*event_cb)(const struct _lv_obj_class_t * class_p,
struct _lv_event_t * e); /**< Widget type specific event function*/
lv_coord_t width_def;
lv_coord_t height_def;
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
} lv_obj_class_t;
创建一个 button 的时候,首先调用 lv_obj_class_create_obj 函数,然后再调用 lv_obj_class_init_obj;
lv_btn_create
|--> lv_obj_class_create_obj
|--> lv_obj_class_init_obj
一个是创建 obj,另一个是初始化 obj;
来看一下 lv_obj_class_create_obj:
lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
{
LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
uint32_t s = get_instance_size(class_p);
lv_obj_t * obj = lv_mem_alloc(s);
if(obj == NULL) return NULL;
lv_memset_00(obj, s);
obj->class_p = class_p;
obj->parent = parent;
/*Create a screen*/
if(parent == NULL) {
LV_TRACE_OBJ_CREATE("creating a screen");
lv_disp_t * disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("No display created yet. No place to assign the new screen");
return NULL;
}
if(disp->screens == NULL) {
disp->screens = lv_mem_alloc(sizeof(lv_obj_t *));
disp->screens[0] = obj;
disp->screen_cnt = 1;
}
else {
disp->screen_cnt++;
disp->screens = lv_mem_realloc(disp->screens, sizeof(lv_obj_t *) * disp->screen_cnt);
disp->screens[disp->screen_cnt - 1] = obj;
}
/*Set coordinates to full screen size*/
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;
}
/*Create a normal object*/
else {
LV_TRACE_OBJ_CREATE("creating normal object");
LV_ASSERT_OBJ(parent, MY_CLASS);
if(parent->spec_attr == NULL) {
lv_obj_allocate_spec_attr(parent);
}
if(parent->spec_attr->children == NULL) {
parent->spec_attr->children = lv_mem_alloc(sizeof(lv_obj_t *));
parent->spec_attr->children[0] = obj;
parent->spec_attr->child_cnt = 1;
}
else {
parent->spec_attr->child_cnt++;
parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
}
}
return obj;
}
主要就是创建了 obj 对象;
下面调用 lv_obj_class_init_obj 对 obj 进行初始化:
void lv_obj_class_init_obj(lv_obj_t * obj)
{
// 标记 screen 为 dirty (设置 scr->scr_layout_inv = 1),在更新 layout 的时候会用到;
lv_obj_mark_layout_as_dirty(obj);
// 禁止 style 刷新
lv_obj_enable_style_refresh(false);
lv_theme_apply(obj);
// 调用对应组件的构造函数;
lv_obj_construct(obj);
// 使能 style 刷新
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
lv_obj_refresh_self_size(obj);
lv_group_t * def_group = lv_group_get_default();
if(def_group && lv_obj_is_group_def(obj)) {
lv_group_add_obj(def_group, obj);
}
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
/*Call the ancestor's event handler to the parent to notify it about the new child.
*Also triggers layout update*/
lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj);
/*Invalidate the area if not screen created*/
// 标记为 obj 无效,即需要被更新
lv_obj_invalidate(obj);
}
}
屏幕,是没有父类的基础对象;
小结
所以,可以看到,通过如下方式进行创建 button 的时候 (打个比方)
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_height(btn, 30);
那么这个 button 的 parent 就会被指定为当前的 scr_act 屏幕;