LVGL (6) 基础对象 Obj 分析

本文深入解析LVGL图形库中对象系统的实现,以button为例,详细介绍了如何使用lv_obj_t结构创建和初始化对象。LVGL通过lv_obj_class_t结构定义组件类,包括构造和析构函数,以及默认属性。lv_btn_create首先调用lv_obj_class_create_obj创建对象,再通过lv_obj_class_init_obj进行初始化,包括设置父对象、调用构造函数和触发相关事件。整个过程展示了LVGL组件的抽象和继承机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 屏幕;

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值