LVGL:基础对象

编译好的lvgl库:下载地址

下面代码用到的一个函数:

/**
 * 初始化 LVGL 图形库的硬件抽象层(Hardware Abstraction Layer,HAL)
 */
static void hal_init(void)
{
    // 使用 'monitor' 驱动,在 PC 的显示器上创建窗口以模拟显示器
    monitor_init();

    // Tick 初始化。
    // 需要定期调用 'lv_tick_inc()' 函数通知 LVGL 已经过去了多少时间,
    // 创建一个 SDL 线程来执行这个操作
    SDL_CreateThread(tick_thread, "tick", NULL);

    // 创建一个显示缓冲区disp_buf1
    // 并根据屏幕的水平分辨率和垂直分辨率,分配两个用于缓冲区的数组 buf1_1 和 buf1_2。
    static lv_disp_draw_buf_t disp_buf1;
    static lv_color_t buf1_1[MONITOR_HOR_RES * 100];
    static lv_color_t buf1_2[MONITOR_HOR_RES * 100];
    // 初始化一个显示缓冲区
    lv_disp_draw_buf_init(&disp_buf1, buf1_1, buf1_2, MONITOR_HOR_RES * 100);

    // 创建一个显示器
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv); // 基本初始化
    disp_drv.draw_buf = &disp_buf1;
    disp_drv.flush_cb = monitor_flush;
    disp_drv.hor_res = MONITOR_HOR_RES;//800
    disp_drv.ver_res = MONITOR_VER_RES;//480
    disp_drv.antialiasing = 1;//抗锯齿

    // 注册显示器驱动器
    lv_disp_t * disp = lv_disp_drv_register(&disp_drv);

    lv_theme_t * th = lv_theme_default_init(disp,
                                           lv_palette_main(LV_PALETTE_BLUE),
                                           lv_palette_main(LV_PALETTE_RED),
                                           LV_THEME_DEFAULT_DARK,
                                           LV_FONT_DEFAULT);
    lv_disp_set_theme(disp, th);//给显示器设置主题

    lv_group_t * g = lv_group_create();  // 创建一个组
    lv_group_set_default(g);             // 设置为默认组

    // 添加鼠标作为输入设备
    // 使用 'mouse' 驱动,用来读取 PC 的鼠标
    mouse_init();
    static lv_indev_drv_t indev_drv_1;
    lv_indev_drv_init(&indev_drv_1); // 输入设备初始化
    indev_drv_1.type = LV_INDEV_TYPE_POINTER;//类型为鼠标
    indev_drv_1.read_cb = mouse_read;// 这个函数会被周期性地调用(由库来调用),用于获取鼠标的位置和状态

    lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);//注册鼠标设备

    //添加键盘作为输入设备
    keyboard_init();
    static lv_indev_drv_t indev_drv_2;
    lv_indev_drv_init(&indev_drv_2); // 基本初始化
    indev_drv_2.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv_2.read_cb = keyboard_read;
    lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv_2);
    lv_indev_set_group(kb_indev, g); // 设置键盘输入设备所属的组

    //添加鼠标滚轮作为输入设备
    mousewheel_init();
    static lv_indev_drv_t indev_drv_3;
    lv_indev_drv_init(&indev_drv_3); // 基本初始化
    indev_drv_3.type = LV_INDEV_TYPE_ENCODER;
    indev_drv_3.read_cb = mousewheel_read;
    lv_indev_t * enc_indev = lv_indev_drv_register(&indev_drv_3);
    lv_indev_set_group(enc_indev, g); // 设置编码器输入设备所属的组

    // 为鼠标设置一个光标
    LV_IMG_DECLARE(mouse_cursor_icon); // 声明图像文件
    lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); // 为光标创建一个图像对象
    lv_img_set_src(cursor_obj, &mouse_cursor_icon); // 设置图像的来源
    lv_indev_set_cursor(mouse_indev, cursor_obj); // 将图像对象连接到输入设备
}

lv_obj_t 可以直接用作一个简单矩形组件。

一、坐标

1.1、单位和大小

1、像素。

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *parent = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器
    lv_obj_t *obj = lv_obj_create(parent);  // 创建一个对象

    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj, &label_style, LV_PART_MAIN); // 修改这里的参数顺序

    lv_obj_set_pos(obj, 100, 50);  // 设置对象相对于父容器的位置

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

2、百分比

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *parent = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器
    lv_obj_t *obj = lv_obj_create(parent);  // 创建一个对象

    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj, &label_style, LV_PART_MAIN); // 修改这里的参数顺序

    lv_obj_set_width(obj, lv_pct(50)); // 设置obj宽度为父容器宽度的50%,lv_pct() 会被 LVGL 内部自动根据父容器尺寸解析

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

3、使用 LV_SIZE_CONTENT,则在指定方向上(水平或垂直),对象的尺寸将根据其子对象的尺寸来自动调整。实际上对象的尺寸会适应其右侧和底部子对象的最大尺寸,而顶部和左侧超出的部分会被裁剪(如果对象设置了 LV_OBJ_FLAG_HIDDEN(隐藏)或 LV_OBJ_FLAG_FLOATING(浮动)标志,则在计算 LV_SIZE_CONTENT 时,这些对象将会被忽略)。

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *window = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    lv_obj_t *parent = lv_obj_create(window);  // 创建一个对象
    lv_obj_set_size(parent, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0xFF0000));
    lv_obj_add_style(parent, &label_style, LV_PART_MAIN);
    lv_obj_center(parent);

    // 创建两个子对象并设置不同的大小
    auto child1 = lv_btn_create(parent);
    lv_obj_set_width(child1, 100);
    lv_obj_set_height(child1, 50);

    auto child2 = lv_label_create(parent);
    lv_obj_set_width(child2, 200);

    // 父对象的尺寸现在会适应child2的宽度(因为child2更宽)
    // 而高度则由child1决定(因为它在下方)

    // 添加内边距
    lv_obj_set_style_pad_left(parent, 20, 0);
    lv_obj_set_style_pad_right(parent, 20, 0);
    lv_obj_set_style_pad_top(parent, 20, 0);
    lv_obj_set_style_pad_bottom(parent, 20, 0);

    // 此时parent的实际宽度是:child2的宽度 + 左右内边距
    // 实际高度是:child1的高度 + 上下内边距

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

1.2、盒子模式

lvgl 遵循 CSS 的 border-box 模型。对象的“盒子”由以下部分构成:

  • bounding:元素的宽度/高度围起来的区域。
  • border:边框的宽度。
  • padding:对象两侧与其子对象之间的空间。
  • content:如果边界框按边框宽度和填充的大小缩小,则显示其大小的内容区域。
  • outline:绘制在边界框之外。

1.3、对齐

默认左上角为原点(0,0)。要更改原点使用:

lv_obj_set_align(obj, align);

要更改对齐方式并设置新坐标:

lv_obj_align(obj, align, x, y);

对齐选项:

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *parent = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    {
        lv_obj_t *obj = lv_obj_create(parent);  // 创建一个对象
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        lv_obj_set_pos(obj,10,10);
        lv_obj_set_align(obj,LV_ALIGN_TOP_LEFT);
    }

    {
        lv_obj_t *obj = lv_obj_create(parent);
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0xFF0000));
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        lv_obj_set_pos(obj,10,10);
        lv_obj_set_align(obj,LV_ALIGN_TOP_MID);
    }

    {
        lv_obj_t *obj = lv_obj_create(parent);
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0x00FF00));
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        lv_obj_align(obj,LV_ALIGN_TOP_MID,20,20);
    }

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

将子项对齐到其父项的中心:

lv_obj_center(obj);

//等同于:
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *parent = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    {
        lv_obj_t *obj = lv_obj_create(parent);  // 创建一个对象
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        lv_obj_center(obj);
    }

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *parent = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    lv_obj_t *obj1 = lv_obj_create(parent);  // 创建一个对象
    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj1, &label_style, LV_PART_MAIN);
    lv_obj_center(obj1);

    {
        lv_obj_t *obj = lv_obj_create(parent);  // 创建一个对象
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0xFF0000));
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        // lv_obj_align_to(obj,obj1 , LV_ALIGN_TOP_LEFT, 0, 0);
        lv_obj_align_to(obj,obj1 , LV_ALIGN_OUT_LEFT_TOP, 0, -10);
    }

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

要注意:如果参考对象的坐标发生变化,通过 lv_obj_align() 设置的对齐的对象会跟着变化,而通过 lv_obj_align_to() 设置的则无法重新对齐对象。 

1.4、坐标延迟计算

为了提高性能,lvgl 不会立即重新计算所有坐标变化,而是对象被标记为“脏”。

在重绘屏幕之前 lvgl 会检查是否有“脏”对象。如果有,lvgl 会刷新它们的位置、大小和布局。

强制 lvgl 重新计算坐标。需要 lv_obj_update_layout(obj)。大小和位置可能取决于父级或布局。因此 lv_obj_update_layout() 重新计算obj屏幕上所有对象的坐标。

1.5、拓展点击区域

可以使用 lv_obj_set_ext_click_area()扩展该区域:

static void button_event_handler(lv_event_t * event)
{
    cout << "按下"<<std::endl;
}

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *window = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    lv_obj_t *obj1 = lv_obj_create(window);  // 创建一个对象
    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj1, &label_style, LV_PART_MAIN);
    lv_obj_center(obj1);
    lv_obj_add_event_cb(obj1, button_event_handler,LV_EVENT_CLICKED,0);
    lv_obj_set_ext_click_area(obj1, 50); // 这将使对象在实际边界外增加50像素宽的可点击区域

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

二、父项、子项

可说的不多,看例子:

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *window = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    lv_obj_t *obj1 = lv_obj_create(window);  // 创建一个对象
    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj1, &label_style, LV_PART_MAIN);
    lv_obj_center(obj1);

    {
        lv_obj_t *obj = lv_obj_create(window);  // 创建一个对象
        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0xFF0000));
        lv_obj_add_style(obj, &label_style, LV_PART_MAIN);
        lv_obj_align_to(obj,obj1 , LV_ALIGN_OUT_LEFT_TOP, 0, -10);
    }

    for(uint32_t i = 0; i < lv_obj_get_child_cnt(window); ++i)
    {
        lv_obj_t * child = lv_obj_get_child(window,i);

        static lv_style_t label_style;
        lv_style_init(&label_style);
        lv_style_set_bg_color(&label_style, lv_color_hex(0x00FF00));
        lv_obj_add_style(child, &label_style, LV_PART_MAIN);
    }
    
    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

三、显示器

在 lvgl 中,display 作为最顶级的对象,它下面挂载了多个 screen,而每个 screen 则由一系列图形对象组成,这些对象以特定的布局方式排列,共同形成了可以在 display 上展示的完整图形用户界面。

lvgl 中如何操作和管理屏幕:

  1. 创建并激活屏幕:通过 lv_obj_t * screen = lv_obj_create(NULL) 创建一个屏幕对象后,可以使用 lv_scr_load(screen) 函数将其设为活动(active)屏幕。当前显示的内容将切换到这个指定的屏幕上。
  2. 获取活动屏幕:lv_scr_act() 返回指向当前活动屏幕的指针。
  3. 多显示器支持及默认显示器:如果系统中有多个显示器(display),那么屏幕相关的函数(如 lv_scr_load())会作用于最近创建的显示器上,或者也可以通过 lv_disp_set_default() 显式选择某个显示器作为默认显示器,之后的屏幕操作就会针对这个选定的显示器进行。
  4. 获取对象所属的屏幕:要查询一个 lvgl 对象所关联的屏幕,可以调用 lv_obj_get_screen(obj) 函数,它会返回该对象所在的屏幕对象的指针。

四、可选属性

以下属性可由 lv_obj_add_flag() 和 lv_obj_clear_flag() 函数启用或禁用。它们影响对象的行为和交互特性:

  • LV_OBJ_FLAG_HIDDEN:使对象隐藏。隐藏的对象不会显示在屏幕上。
  • LV_OBJ_FLAG_CLICKABLE:允许输入设备(如鼠标、触摸屏)点击该对象进行交互。
  • LV_OBJ_FLAG_CLICK_FOCUSABLE:当对象被点击时,为其添加焦点状态,使其成为当前焦点对象。
  • LV_OBJ_FLAG_CHECKABLE:当对象被点击时切换其选中状态,适用于复选框等组件。
  • LV_OBJ_FLAG_SCROLLABLE:使对象具有滚动功能,当内容超出边界时可以滚动查看。
  • LV_OBJ_FLAG_SCROLL_ELASTIC:允许对象内部滚动,但滚动速度较慢,且有回弹效果。
  • LV_OBJ_FLAG_SCROLL_MOMENTUM:用户释放滚动后,对象会继续滚动一段时间,类似物理惯性效果。
  • LV_OBJ_FLAG_SCROLL_ONE:只允许滚动一个“可吸附”子对象,适用于只能显示一个子对象的滚动视图。
  • LV_OBJ_FLAG_SCROLL_CHAIN:将滚动事件传递给父对象,使得父容器也能响应滚动动作。
  • LV_OBJ_FLAG_SCROLL_ON_FOCUS:当对象获取焦点时,自动滚动以确保对象可见。
  • LV_OBJ_FLAG_SNAPPABLE:如果父容器启用了滚动吸附功能,则此对象可以作为吸附目标。
  • LV_OBJ_FLAG_PRESS_LOCK:保持对象按下状态,即使按下的初始位置已经滑出对象区域。
  • LV_OBJ_FLAG_EVENT_BUBBLE:将事件同时传递给父对象,实现事件冒泡机制。
  • LV_OBJ_FLAG_GESTURE_BUBBLE:将手势事件传递给父对象。
  • LV_OBJ_FLAG_ADV_HITTEST:执行更精确的命中测试(点击测试),例如考虑圆角等因素。
  • LV_OBJ_FLAG_IGNORE_LAYOUT:让布局管理器忽略此对象,允许自定义对象的位置。
  • LV_OBJ_FLAG_FLOATING:当父对象滚动时,此对象不随父对象一起滚动,并且忽略布局约束。
  • 还有几个自定义标识,略。
static void button_event_handler(lv_event_t * event)
{
    cout << "按下"<<std::endl;
}

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    lv_obj_t *window = lv_disp_get_scr_act(NULL);  // 获取当前显示器的活动屏幕对象作为父容器

    lv_obj_t *obj1 = lv_obj_create(window);  // 创建一个对象
    static lv_style_t label_style;
    lv_style_init(&label_style);
    lv_style_set_bg_color(&label_style, lv_color_hex(0x0000FF)); // 蓝色
    lv_obj_add_style(obj1, &label_style, LV_PART_MAIN);
    lv_obj_center(obj1);
    lv_obj_clear_flag(obj1, LV_OBJ_FLAG_CLICKABLE); // 设置不可点击
    lv_obj_add_event_cb(obj1, button_event_handler,LV_EVENT_CLICKED,0);//点击时触发事件

    while (1)
    {
        lv_task_handler();
        usleep(5 * 1000);
    }

    return 0;
}

五、控件状态

状态可由 lv_obj_add_state() 和  lv_obj_clear_state() 设置。

  • LV_STATE_DEFAULT:默认状态,表示对象处于其基本、自然的状态。
  • LV_STATE_CHECKED:选中状态,适用于具有可勾选功能(如复选框、切换按钮等)的控件。
  • LV_STATE_FOCUSED:焦点状态,当控件获得输入焦点时的状态,通常表现为高亮或其他视觉提示。
  • LV_STATE_FOCUS_KEY:焦点键状态,这可能与特定于键盘导航相关联,但并不常见于所有UI框架。
  • LV_STATE_EDITED:编辑状态,表明用户正在编辑或已编辑过该控件的内容。
  • LV_STATE_HOVERED:悬停状态,当鼠标指针位于控件上方而未点击时的状态。
  • LV_STATE_PRESSED:按下状态,例如在用户点击或触摸控件时触发的状态。
  • LV_STATE_SCROLLED:滚动状态,表示控件内容已被滚动,通常用于列表、滚轮或其他可滚动组件。
  • LV_STATE_DISABLED:禁用状态,控件无法被交互和激活。
  • LV_STATE_USER_1~LV_STATE_USER_4:自定义状态,为开发者预留的四个扩展状态,可以根据应用程序需求自定义行为和样式。
  • LV_STATE_ANY:特殊值,可以用于某些函数以指定任何状态或同时作用于所有状态。这意味着如果某个操作或者样式设置需要针对控件的所有可能状态生效,就可以使用此枚举值。

六、函数

1、void lv_init(void)

这个函数用于初始化LVGL图形库。在使用任何LVGL相关的功能之前,必须首先调用此函数以确保内部数据结构和系统资源得到正确的设置和分配。它通常在应用程序启动时被调用一次,以准备LVGL库提供图形界面服务。

2、void lv_deinit(void)

这个函数用来反初始化LVGL库,释放由 lv_init()函数分配的资源。在不使用自定义内存分配器或者启用垃圾回收的情况下,LVGL库可以正确地执行反初始化操作。这意味着当程序不再需要LVGL的功能并且希望释放相关资源时,应调用此函数。

3、bool lv_is_initialized(void)

此函数返回一个布尔值,表示LVGL库是否当前已经初始化完成。在代码中调用此函数,可以帮助开发者检查是否可以安全地开始使用LVGL提供的各种图形接口。

4、lv_obj_t *lv_obj_create(lv_obj_t *parent)

此函数用于创建一个基础对象,即一个矩形。在LVGL中,所有图形组件都是基于这个基础对象构建的。

参数指向父对象的指针,如果为NULL,则会在当前屏幕(display)上创建一个顶层对象,通常被视为一个新的屏幕或者页面。

5、void lv_obj_add_flag(lv_obj_t *obj, lv_obj_flag_t f) / 

      void lv_obj_clear_flag(lv_obj_t *obj, lv_obj_flag_t f)

此函数用于向给定的对象添加 / 清除一组标志位。

      bool lv_obj_has_flag(const lv_obj_t *obj, lv_obj_flag_t f)

检查一个对象是否设置了给定的标志位。

      bool lv_obj_has_flag_any(const lv_obj_t *obj, lv_obj_flag_t f)

检查对象是否至少设置了提供的标志中的任意一个。

6、void lv_obj_add_state(lv_obj_t *obj, lv_state_t state) / 

      void lv_obj_clear_state(lv_obj_t *obj, lv_state_t state)

此函数用于向对象添加 / 清除状态。

      lv_state_t lv_obj_get_state(const lv_obj_t *obj)

获取对象的状态,返回值为一个包含多个状态(通过按位或运算组合)的枚举值。

      bool lv_obj_has_state(const lv_obj_t *obj, lv_state_t state)

检查对象是否处于特定的状态或一组状态组合。

7、void lv_obj_set_user_data(lv_obj_t *obj, void *user_data)

将自定义数据关联到对象上。

struct user_data_t
{
    int user_id;
    char *username;
};

void set_obj_user_data_example(lv_obj_t *obj, int id, const char *name)
{
    // 创建一个用户数据实例
    user_data_t *user_data = new user_data_t;
    user_data->user_id = id;
    user_data->username = strdup(name); // 这里假设strdup成功分配内存

    lv_obj_set_user_data(obj, user_data);
}

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    {
        lv_obj_t *my_button = lv_btn_create(lv_scr_act());
        set_obj_user_data_example(my_button, 12345, "UserA");

        if(user_data_t *button_data = (user_data_t *)lv_obj_get_user_data(my_button))
        {
            printf("Button is associated with user ID: %d and username: %s\n", button_data->user_id, button_data->username);
        }
    }
}

      void * lv_obj_get_user_data(lv_obj_t *obj)

获取与对象关联的用户自定义数据指针。 

8、void * lv_obj_get_group(const lv_obj_t *obj)

获取对象所属的组的指针。在LVGL中,可以通过组来实现按钮组、事件分发等功能。

9、void lv_obj_allocate_spec_attr(lv_obj_t *obj)

如果尚未分配特殊属性,则为对象分配特殊数据区域。

typedef struct 
{
    int custom_attr1;
    float custom_attr2;
} custom_attr_t;

int main(int argc, char **argv)
{
    lv_init();
    hal_init();

    {
        lv_obj_t *obj = lv_obj_create(lv_scr_act());

        // 为自定义对象分配特殊属性内存
        lv_obj_allocate_spec_attr(obj);

        // 将分配的内存转换为自定义的特殊属性结构体,并初始化它
        custom_attr_t *spec_attr = (custom_attr_t *)obj->spec_attr;
        spec_attr->custom_attr1 = 111;
        spec_attr->custom_attr2 = 3.14;

        // 使用特定属性
        printf("custom_attr1: %d\n", spec_attr->custom_attr1);
        printf("custom_attr2: %f\n", spec_attr->custom_attr2);
    }
}

此函数在LVGL库中主要用于为对象分配额外的特殊属性内存。LVGL的基础对象(lv_obj_t)具有通用的属性和方法,但有时候开发者需要为特定类型的对象扩展额外的功能或存储附加数据。为此,LVGL允许通过 lv_obj_allocate_spec_attr()为每个对象分配一块特定大小的内存空间,这块内存可以用来保存自定义的数据结构。

设计这个函数的原因主要有以下几点:

  1. 扩展性:允许用户根据需要扩展对象功能,无需修改LVGL库的核心代码。
  2. 灵活性:不同类型的组件可以根据实际需求存储不同类型和数量的数据,使得LVGL能够支持更丰富的定制化界面元素。
  3. 高效性:由于内存是在对象创建时就分配好的,并且直接关联到对象上,访问这些特殊属性时效率较高。

lv_obj_allocate_spec_attr() 和 lv_obj_set_user_data() 比较:

这两个函数都可以用于在LVGL对象上存储额外的数据,但它们的设计目的和使用场景有所不同:

  • lv_obj_set_user_data():这个函数允许将任意类型的指针关联到LVGL对象上,这个指针可以指向自定义数据结构或其他需要与对象关联的任何类型的数据。通常用于为对象附加少量、通用的数据。
  • lv_obj_allocate_spec_attr():这个函数是在对象内部分配一段固定大小的内存空间,并将其存储在对象的 spec_attr 成员中。它适用于扩展对象类以添加特定于该类的特殊属性,例如,如果要创建一个新的组件并且希望为其存储一些特定的数据,那么可以通过这种方式预先分配好所需的空间。

总结起来,两者的主要区别在于:

  • lv_obj_set_user_data() 更加灵活,因为它可以存储任何类型的用户数据,但不提供预定义的结构或字段。
  • lv_obj_allocate_spec_attr() 是为扩展LVGL对象而设计的,主要用于为自定义组件分配固定的、特定用途的数据区域,这样可以直接在对象内访问这些数据,并且可以通过类型转换明确知道数据的结构。

10、bool lv_obj_check_type(const lv_obj_t *obj, const lv_obj_class_t *class_p)

检查对象是否属于指定类(类型)。

        bool is_button = lv_obj_check_type(obj2, &lv_btn_class);

11、bool lv_obj_has_class(const lv_obj_t *obj, const lv_obj_class_t *class_p)

检查对象及其祖先类中是否有给定的类(类型)。

12、const lv_obj_class_t * lv_obj_get_class(const lv_obj_t *obj)

获取对象的类(类型),即获取对象实际类型的指针。

        // 创建一个屏幕(或窗口)
        lv_obj_t *scr = lv_disp_get_scr_act(NULL); // 获取当前激活的屏幕

        // 在屏幕上创建一个按钮
        lv_obj_t *btn = lv_btn_create(scr);

        // 获取并检查对象的类
        const lv_obj_class_t *obj_class = lv_obj_get_class(btn);

        // 检查该类是否等于lv_btn的类结构体
        if (obj_class == &lv_btn_class) {
            printf("The object is a button.\n");
        } else {
            printf("The object is not a button.\n");
        }

13、bool lv_obj_is_valid(const lv_obj_t *obj)

检查对象是否仍然有效(未被销毁)。

14、lv_coord_t lv_obj_dpx(const lv_obj_t *obj, lv_coord_t n)

根据对象所在显示设备的DPI(每英寸点数)对像素数量进行缩放,确保在不同DPI的屏幕上具有相同的物理尺寸。它会根据对象所关联的显示器DPI计算出相对于160 DPI显示屏的相应像素距离,并返回结果。

根据当前屏幕的DPI来创建一个宽度固定为逻辑像素宽度100的矩形,并确保其在不同DPI下都能保持相同的物理尺寸:

        // 获取当前激活的屏幕
        lv_obj_t *scr = lv_disp_get_scr_act(NULL);

        // 假设我们想要创建一个宽度为100逻辑像素的边框,使其在任何DPI下都保持物理尺寸一致
        lv_coord_t logical_width = 100; // 逻辑像素宽度

        // 将逻辑像素宽度转换为基于当前屏幕DPI的物理像素宽度
        lv_coord_t physical_width = lv_obj_dpx(scr, logical_width);

        // 创建一个矩形,并设置其宽度为转换后的物理像素宽度
        lv_obj_t * obj2 = lv_obj_create(scr);

        lv_obj_set_size(obj2, physical_width, LV_DPI_DEF / 2); // 高度设置为屏幕DPI的一半作为示例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值