事件

在 LVGL 中,当某些可能对用户感兴趣的事情发生时会触发事件,例如当一个对象

  • 被点击

  • 被滚动

  • 其值发生变化

  • 被重绘等。

为对象添加事件

用户可以为对象分配回调函数以监听其事件。在实践中,它看起来像这样:

lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL);   /*分配一个事件回调*/

...

static void my_event_cb(lv_event_t * event)
{
    printf("Clicked\n");
}

在这个例子中,LV_EVENT_CLICKED 表示只有点击事件会调用 my_event_cb。查看事件代码列表以了解所有选项。 LV_EVENT_ALL 可用于接收所有事件。

最后一个参数 lv_obj_add_event_cb 是一个指向任何自定义数据的指针,该数据将在事件中可用。稍后将更详细地描述它。

可以向对象添加更多事件,如下所示:

lv_obj_add_event_cb(obj, my_event_cb_1, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(obj, my_event_cb_2, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(obj, my_event_cb_3, LV_EVENT_ALL, NULL);		 /*无过滤,接收所有事件*/

甚至可以在一个对象上使用相同的事件回调,但具有不同的 user_data。例如:

lv_obj_add_event_cb(obj, increment_on_click, LV_EVENT_CLICKED, &num1);
lv_obj_add_event_cb(obj, increment_on_click, LV_EVENT_CLICKED, &num2);

事件将按照添加的顺序被调用。

其他对象也可以使用相同的事件回调

从对象中移除事件

可以使用 lv_obj_remove_event_cb(obj, event_cb) 函数或 lv_obj_remove_event_dsc(obj, event_dsc) 从对象中移除事件。event_dsclv_obj_add_event_cb 返回的指针。

事件代码

事件代码可以分为以下几类:

  • 输入设备事件

  • 绘图事件

  • 其他事件

  • 特殊事件

  • 自定义事件

所有对象(如按钮/标签/滑块等)无论其类型如何,都会接收输入设备绘图其他事件。

然而,特殊事件是特定于某个特定小部件类型的。请参阅小部件文档以了解它们何时被发送。

自定义事件由用户添加,LVGL 永远不会发送这些事件。

以下是现有的事件代码:

输入设备事件

  • LV_EVENT_PRESSED 对象被按下

  • LV_EVENT_PRESSING 对象正在被按下(按下时连续调用)

  • LV_EVENT_PRESS_LOST 对象仍在被按下,但光标/手指滑出了对象

  • LV_EVENT_SHORT_CLICKED 对象被按下了一段短时间,然后释放。如果滚动则不会调用。

  • LV_EVENT_LONG_PRESSED 对象已被按下至少由输入设备驱动程序指定的 long_press_time。如果滚动则不会调用。

  • LV_EVENT_LONG_PRESSED_REPEATlong_press_time 之后的每 long_press_repeat_time 毫秒调用。如果滚动则不会调用。

  • LV_EVENT_CLICKED 如果对象没有滚动(无论长按与否),在释放时调用

  • LV_EVENT_RELEASED 在任何情况下,当对象被释放时调用

  • LV_EVENT_SCROLL_BEGIN 滚动开始。事件参数为 NULL 或带有滚动动画描述符的 lv_anim_t *,如果需要可以修改。

  • LV_EVENT_SCROLL_END 滚动结束。

  • LV_EVENT_SCROLL 对象被滚动

  • LV_EVENT_GESTURE 检测到手势。使用 lv_indev_get_gesture_dir(lv_indev_get_act()); 获取手势

  • LV_EVENT_KEY 向对象发送一个键。使用 lv_indev_get_key(lv_indev_get_act()); 获取键

  • LV_EVENT_FOCUSED 对象被聚焦

  • LV_EVENT_DEFOCUSED 对象失去焦点

  • LV_EVENT_LEAVE 对象失去焦点但仍被选中

  • LV_EVENT_HIT_TEST 执行高级命中测试。使用 lv_hit_test_info_t * a = lv_event_get_hit_test_info(e) 并检查 a->point 是否可以点击对象。如果不能,设置 a->res = false

绘图事件

  • LV_EVENT_COVER_CHECK 检查对象是否完全覆盖一个区域。事件参数为 lv_cover_check_info_t *

  • LV_EVENT_REFR_EXT_DRAW_SIZE 获取对象周围所需的额外绘图区域(例如阴影)。事件参数为 lv_coord_t * 用于存储大小。仅用更大的值覆盖它。

  • LV_EVENT_DRAW_MAIN_BEGIN 开始主绘图阶段。

  • LV_EVENT_DRAW_MAIN 执行主绘图

  • LV_EVENT_DRAW_MAIN_END 完成主绘图阶段

  • LV_EVENT_DRAW_POST_BEGIN 开始后绘图阶段(当所有子对象都被绘制时)

  • LV_EVENT_DRAW_POST 执行后绘图阶段(当所有子对象都被绘制时)

  • LV_EVENT_DRAW_POST_END 完成后绘图阶段(当所有子对象都被绘制时)

  • LV_EVENT_DRAW_PART_BEGIN 开始绘制一个部分。事件参数为 lv_obj_draw_dsc_t *。了解更多这里

  • LV_EVENT_DRAW_PART_END 完成绘制一个部分。事件参数为 lv_obj_draw_dsc_t *。了解更多这里

LV_EVENT_DRAW_... 事件中,不允许调整小部件的属性。例如,不能调用 lv_obj_set_width()。 换句话说,只能调用 get 函数。

其他事件

  • LV_EVENT_DELETE 对象正在被删除

  • LV_EVENT_CHILD_CHANGED 子对象被移除/添加

  • LV_EVENT_CHILD_CREATED 子对象被创建,总是冒泡到所有父对象

  • LV_EVENT_CHILD_DELETED 子对象被删除,总是冒泡到所有父对象

  • LV_EVENT_SIZE_CHANGED 对象坐标/大小已更改

  • LV_EVENT_STYLE_CHANGED 对象的样式已更改

  • LV_EVENT_BASE_DIR_CHANGED 基本方向已更改

  • LV_EVENT_GET_SELF_SIZE 获取小部件的内部大小

  • LV_EVENT_SCREEN_UNLOAD_START 屏幕卸载开始,当调用 lv_scr_load/lv_scr_load_anim 时立即触发

  • LV_EVENT_SCREEN_LOAD_START 屏幕加载开始,当屏幕更改延迟到期时触发

  • LV_EVENT_SCREEN_LOADED 屏幕已加载,当所有动画完成时调用

  • LV_EVENT_SCREEN_UNLOADED 屏幕已卸载,当所有动画完成时调用

特殊事件

  • LV_EVENT_VALUE_CHANGED 对象的值已更改(即滑块移动)

  • LV_EVENT_INSERT 文本正在插入到对象中。事件数据是正在插入的 char *

  • LV_EVENT_REFRESH 通知对象刷新其上的某些内容(供用户使用)

  • LV_EVENT_READY 进程已完成

  • LV_EVENT_CANCEL 进程已取消

自定义事件

可以通过 uint32_t MY_EVENT_1 = lv_event_register_id(); 注册任何自定义事件代码。

可以使用 lv_event_send(obj, MY_EVENT_1, &some_data) 将它们发送到任何对象。

发送事件

要手动向对象发送事件,请使用 lv_event_send(obj, <EVENT_CODE> &some_data)

例如,这可以用来通过模拟按钮按下手动关闭消息框(尽管有更简单的方法可以做到这一点):

/*模拟按下第一个按钮(索引从零开始)*/
uint32_t btn_id = 0;
lv_event_send(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);

刷新事件

LV_EVENT_REFRESH 是一个特殊事件,因为它旨在让用户通知对象刷新自身。一些示例:

  • 通知标签根据一个或多个变量(例如当前时间)刷新其文本

  • 在语言更改时刷新标签

  • 如果满足某些条件(例如输入正确的 PIN),启用按钮

  • 如果超出限制,向对象添加/移除样式等

lv_event_t 的字段

lv_event_t 是传递给事件回调的唯一参数,它包含有关事件的所有数据。可以从中获取以下值:

  • lv_event_get_code(e) 获取事件代码

  • lv_event_get_current_target(e) 获取事件发送到的对象。即正在调用其事件处理程序的对象。

  • lv_event_get_target(e) 获取最初触发事件的对象(如果启用了事件冒泡,则与 lv_event_get_target 不同)

  • lv_event_get_user_data(e) 获取作为 lv_obj_add_event_cb 的最后一个参数传递的指针。

  • lv_event_get_param(e) 获取作为 lv_event_send 的最后一个参数传递的参数。

事件冒泡

如果启用了 lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE),所有事件也会发送到对象的父对象。如果父对象也启用了 LV_OBJ_FLAG_EVENT_BUBBLE,事件将发送到其父对象,依此类推。

事件的目标参数始终是当前目标对象,而不是原始对象。要在事件处理程序中获取原始目标,请调用 lv_event_get_original_target(e)

示例

按钮点击事件

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void event_cb(lv_event_t * e)
{
    LV_LOG_USER("Clicked");

    static uint32_t cnt = 1;
    lv_obj_t * btn = lv_event_get_target(e);
    lv_obj_t * label = lv_obj_get_child(btn, 0);
    lv_label_set_text_fmt(label, "%"LV_PRIu32, cnt);
    cnt++;
}

/**
 * Add click event to a button
 */
void lv_example_event_1(void)
{
    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 100, 50);
    lv_obj_center(btn);
    lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL);

    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, "Click me!");
    lv_obj_center(label);
}

#endif

MicroPython code  

 GitHub Simulator
class Event_1():
    def __init__(self):
        self.cnt = 1
        #
        # Add click event to a button
        #

        btn = lv.btn(lv.scr_act())
        btn.set_size(100, 50)
        btn.center()
        btn.add_event_cb(self.event_cb, lv.EVENT.CLICKED, None)

        label = lv.label(btn)
        label.set_text("Click me!")
        label.center()

    def event_cb(self,e):
        print("Clicked")

        btn = e.get_target()
        label = btn.get_child(0)
        label.set_text(str(self.cnt))
        self.cnt += 1

evt1 = Event_1()

处理多个事件

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_SWITCH

static void event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * label = lv_event_get_user_data(e);

    switch(code) {
        case LV_EVENT_PRESSED:
            lv_label_set_text(label, "The last button event:\nLV_EVENT_PRESSED");
            break;
        case LV_EVENT_CLICKED:
            lv_label_set_text(label, "The last button event:\nLV_EVENT_CLICKED");
            break;
        case LV_EVENT_LONG_PRESSED:
            lv_label_set_text(label, "The last button event:\nLV_EVENT_LONG_PRESSED");
            break;
        case LV_EVENT_LONG_PRESSED_REPEAT:
            lv_label_set_text(label, "The last button event:\nLV_EVENT_LONG_PRESSED_REPEAT");
            break;
        default:
            break;
    }
}

/**
 * Handle multiple events
 */
void lv_example_event_2(void)
{
    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_set_size(btn, 100, 50);
    lv_obj_center(btn);

    lv_obj_t * btn_label = lv_label_create(btn);
    lv_label_set_text(btn_label, "Click me!");
    lv_obj_center(btn_label);

    lv_obj_t * info_label = lv_label_create(lv_scr_act());
    lv_label_set_text(info_label, "The last button event:\nNone");

    lv_obj_add_event_cb(btn, event_cb, LV_EVENT_ALL, info_label);
}

#endif

MicroPython code  

 GitHub Simulator
def event_cb(e,label):
    code = e.get_code()
    if code == lv.EVENT.PRESSED:
        label.set_text("The last button event:\nLV_EVENT_PRESSED")
    elif code == lv.EVENT.CLICKED:
        label.set_text("The last button event:\nLV_EVENT_CLICKED")
    elif code == lv.EVENT.LONG_PRESSED:
        label.set_text("The last button event:\nLV_EVENT_LONG_PRESSED")
    elif code == lv.EVENT.LONG_PRESSED_REPEAT:
        label.set_text("The last button event:\nLV_EVENT_LONG_PRESSED_REPEAT")
btn = lv.btn(lv.scr_act())
btn.set_size(100, 50)
btn.center()

btn_label = lv.label(btn)
btn_label.set_text("Click me!")
btn_label.center()

info_label = lv.label(lv.scr_act())
info_label.set_text("The last button event:\nNone")

btn.add_event_cb(lambda e: event_cb(e,info_label), lv.EVENT.ALL, None)

事件冒泡

C code  

 GitHub
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX

static void event_cb(lv_event_t * e)
{
    /*The original target of the event. Can be the buttons or the container*/
    lv_obj_t * target = lv_event_get_target(e);

    /*The current target is always the container as the event is added to it*/
    lv_obj_t * cont = lv_event_get_current_target(e);

    /*If container was clicked do nothing*/
    if(target == cont) return;

    /*Make the clicked buttons red*/
    lv_obj_set_style_bg_color(target, lv_palette_main(LV_PALETTE_RED), 0);
}

/**
 * Demonstrate event bubbling
 */
void lv_example_event_3(void)
{

    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 290, 200);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

    uint32_t i;
    for(i = 0; i < 30; i++) {
        lv_obj_t * btn = lv_btn_create(cont);
        lv_obj_set_size(btn, 80, 50);
        lv_obj_add_flag(btn, LV_OBJ_FLAG_EVENT_BUBBLE);

        lv_obj_t * label = lv_label_create(btn);
        lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
        lv_obj_center(label);
    }

    lv_obj_add_event_cb(cont, event_cb, LV_EVENT_CLICKED, NULL);
}

#endif

MicroPython code  

 GitHub Simulator
def event_cb(e):

    # The original target of the event. Can be the buttons or the container
    target = e.get_target()
    # print(type(target))

    # If container was clicked do nothing
    if type(target) != type(lv.btn()):
        return

    # Make the clicked buttons red
    target.set_style_bg_color(lv.palette_main(lv.PALETTE.RED), 0)

#
# Demonstrate event bubbling
#

cont = lv.obj(lv.scr_act())
cont.set_size(320, 200)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(30):
    btn = lv.btn(cont)
    btn.set_size(80, 50)
    btn.add_flag(lv.obj.FLAG.EVENT_BUBBLE)

    label = lv.label(btn)
    label.set_text(str(i))
    label.center()

cont.add_event_cb(event_cb, lv.EVENT.CLICKED, None)