【LVGL(4)】对象的事件及事件冒泡

LVGL官方文档:Welcome to the documentation of LVGL! — LVGL documentationhttps://docs.lvgl.io/master/index.html


事件(events)

点击、滚动、数组改变、重绘……

事件类型

  • 输入设备事件(Input device events)
  • 绘图事件(Drawing events)
  • 其他事件(Special events)
  • 特殊事件(Other events)
  • 自定义事件(Custom events)

看文档啦 ~ Events — LVGL documentation

添加事件

lv_obj_add_event_cb(obj, event_cb, event_code, user_data);

参数:对象,处理(cb:callbackfunction,回调函数),事件类型,用户数据

发送事件

lv_event_send(obj, event_cb, event_code, user_data);

删除事件

lv_obj_remove_event_cb(obj, event_cb);
lv_obj_remove_event_dsc(obj, event_dsc);    //event_dsc 是 lv_obj_add_event_cb 返回的指针

最简单的事件:

static void function()
{
    printf("test\n");
}

void mode(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    
    lv_obj_add_event_cb(obj,function,LV_EVENT_CLICKED,NULL);    //按键点击,释放后调用
}

被调用的函数是可以传入参数的

function(lv_event_t * e)
typedef struct _lv_event_t {
    struct _lv_obj_t * target;    //触发事件的对象
    struct _lv_obj_t * current_target;    //父级触发事件的对象
    lv_event_code_t code;    //事件代码
    void * user_data;    //用户数据
    void * param;
    struct _lv_event_t * prev;
    uint8_t deleted : 1;
    uint8_t stop_processing : 1;
    uint8_t stop_bubbling : 1;
} lv_event_t;

如下,写了个点灯的接口

int count = 0;
static void function1(lv_event_t * e)
{


    lv_obj_t * obj = lv_event_get_target(e);        // 获取触发事件的部件(对象)
    lv_event_code_t code = lv_event_get_code(e);    // 获取当前部件(对象)触发的事件代码
    lv_obj_t * label = lv_event_get_user_data(e);   // 获取添加事件时传递的用户数据


    if(count == 0 && code == LV_EVENT_PRESSED)
    {
        lv_label_set_text(label, "OPEN");
        lv_obj_set_style_bg_color(obj, lv_color_hex(0xc43e1c), 0);  // 通过本地样式(私有样式)设置背景色
        printf("OPEN\n");
        count ++;
    }
    else if(count == 1 && code == LV_EVENT_PRESSED)
    {
        lv_label_set_text(label, "CLOSE");
        lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffff), 0);  // 通过本地样式(私有样式)设置背景色
        printf("CLOSE\n");
        count ++;
    }
    if(count==2)
        count = 0;

}


void lv_100ask_demo_course_2_2_6(void)
{

    /* 创建基础部件(对象) */
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_center(obj);    //放在中心

    /* 创建label部件(对象) */
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "CLOSE");   // 设置label展示的文字
    lv_obj_center(label);               // 将对象与其父对象的中心对齐,这里的父对象是屏幕:lv_scr_act()

    // 为obj1添加事件回调函数,所有的事件类型都能触发该回调函数
    lv_obj_add_event_cb(obj, function1, LV_EVENT_ALL, label);//传入的是label,可设置label
}

现象:

点击开灯:

 点击关灯:


事件冒泡

如果对象启用了 lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE),该对象的所有事件将会发送到该对象的父级。如果父级也启用了 LV_OBJ_FLAG_EVENT_BUBBLE,那么事件继续发送到他的父级,依此类推。

  • lv_event_get_target(e); 获取触发事件的当前对象。 
  • lv_event_get_current_target(e); 获取事件冒泡的父对象。
static void my_event_cb(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);            // 获取触发事件的对象
    lv_obj_t * parent = lv_event_get_current_target(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, "LV_EVENT_PRESSED");
            /* 父级 */
            lv_obj_set_style_bg_color(parent, lv_color_hex(0xc43e1c), 0);   // 通过本地样式(私有样式)设置背景色
            /* 触发事件本身对象 */
            lv_obj_set_style_bg_color(obj, lv_color_hex(0xc43e1c), 0);      // 通过本地样式(私有样式)设置背景色
            printf("LV_EVENT_PRESSED\n");
            break;
        case LV_EVENT_CLICKED:    //松开的时候产生了点击
            lv_label_set_text(label, "LV_EVENT_CLICKED");
            lv_obj_remove_local_style_prop(parent, LV_STYLE_BG_COLOR, 0);   // 删除通过本地样式(私有样式)设置的背景色
            lv_obj_remove_local_style_prop(obj, LV_STYLE_BG_COLOR, 0);      // 删除通过本地样式(私有样式)设置的背景色
            printf("LV_EVENT_CLICKED\n");
            break;
        default:
            //printf("NONE\n");
            break;
    }
}

void lv_100ask_demo_course_2_2_6(void)
{
    /* 创建一个基础对象 obj1 */
    lv_obj_t * obj1 = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj1, 450, 250);
    lv_obj_center(obj1);                                // 将对象与其父对象的中心对齐,这里的父对象是屏幕:lv_scr_act()

    /* 以 obj1 创建一个基础对象 obj2 */
    lv_obj_t * obj2 = lv_obj_create(obj1);
    lv_obj_set_size(obj2, 400, 200);
    lv_obj_center(obj2);                                // 将对象与其父对象的中心对齐,这里的父对象是屏幕:obj1
    lv_obj_add_flag(obj2, LV_OBJ_FLAG_EVENT_BUBBLE);    // 启用事件冒泡,将接收到的所有事件传播给父级

    /* 以 obj2 创建一个基础对象 obj3 */
    lv_obj_t * obj3 = lv_obj_create(obj2);
    lv_obj_set_size(obj3, 350, 150);
    lv_obj_center(obj3);                                // 将对象与其父对象的中心对齐,这里的父对象是屏幕:obj2
    lv_obj_add_flag(obj3, LV_OBJ_FLAG_EVENT_BUBBLE);    // 启用事件冒泡,将接收到的所有事件传播给父级

    /* 以 obj3 创建一个基础对象 obj4 */
    lv_obj_t * obj4 = lv_obj_create(obj3);
    lv_obj_set_size(obj4, 300, 100);
    lv_obj_center(obj4);                                // 将对象与其父对象的中心对齐,这里的父对象是屏幕:obj3
    lv_obj_add_flag(obj4, LV_OBJ_FLAG_EVENT_BUBBLE);    // 启用事件冒泡,将接收到的所有事件传播给父级

    /* 以屏幕为父类,创建一个label部件(对象) */
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "test");                               // 设置label展示的文字
    lv_obj_align_to(label, obj1, LV_ALIGN_OUT_TOP_MID, 0, 0);       // 将label相对于obj1对齐

    // 将给obj1添加事件回调函数,所有的事件类型都能触发该回调函数
    lv_obj_add_event_cb(obj1, my_event_cb, LV_EVENT_ALL, label);
}

哈哈哈哈 ~ 真的很有意思呢 ~

现象:

点击最中间对象:

点击第三层对象:

 点击第二层对象:

 最外层对象:


事件说明

一个事件回调函数可给多个对象使用

我们创建了一个事件处理函数之后是可以给不同的对象使用的。

一个对象可以使用多个事件回调函数

我们创建的对象可以绑定多个事件,比如一个事件是处理点击类型的事件,一个事件处理按下类型的事件等等。

其他

如果传入的用户数据不一样,一个对象可以绑定同一个事件回调函数多次,事件将按照添加的顺序调用。例如:

  • lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num1);
  • lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num2);

OK,这样就能实现简单的函数调用功能了,至少目前一直到开发板上可以当个点灯开关使用了。

<think>好的,我现在需要帮助用户解决关于LVGL发送事件和触发自定义事件的示例代码问题。首先,我需要理解用户的具体需求。用户提到想了解如何发送事件,特别是触发自定义事件的示例代码。根据用户提供的引用内容,特别是引用[1]、[2]、[3]、[4],我需要结合这些信息来构建回答。 首先,我应该回顾LVGL中的事件处理机制。根据引用[1],事件处理是通过lv_obj_add_event_cb函数注册回调函数,当特定事件发生时触发。例如,LV_EVENT_CLICKED会在对象被点击时调用回调函数。引用[3]提到事件冒泡机制,事件会从子对象传递到父对象,这可能对用户理解事件传播有帮助。 接下来,用户需要的是发送自定义事件的示例代码。在LVGL中,发送事件通常涉及调用lv_event_send函数。我需要确认这个函数的正确用法,包括参数:对象指针、事件代码和可能的参数。引用[2]中的lv_event_get_code和lv_event_get_param可能在处理事件时用到,但用户的问题是关于发送而非接收,所以可能需要更多关注发送的步骤。 用户可能需要自定义事件代码,所以需要说明如何定义LV_EVENT_自定义类型。例如,使用LV_EVENT_LAST + 1来定义新的事件代码,如LV_EVENT_MY_CUSTOM_EVENT。同时,在发送事件时,需要将自定义数据通过lv_event_set_param传递,并在回调函数中使用lv_event_get_param获取。 另外,根据引用[4],Flags可能与对象的状态有关,但用户的问题主要集中在事件发送,所以可能不需要深入讨论Flags,除非事件触发涉及到状态变化。不过,暂时可能不需要涉及这部分。 现在需要构建示例代码。首先,创建对象如按钮,然后注册事件回调函数。在回调函数中,可能需要处理自定义事件。例如,当按钮被点击时,发送一个自定义事件到另一个对象,比如父容器。然后父容器的回调函数处理这个自定义事件。 需要注意的是,用户可能需要多个步骤的代码:定义事件代码、发送事件、处理事件。因此,示例应该分步展示这些内容。同时,确保代码语法正确,特别是LVGL的API调用方式。 可能的问题点:自定义事件是否需要在某个范围内定义,或者是否需要特定的宏。根据LVGL文档,通常自定义事件应该从LV_EVENT_LAST + 1开始定义,以确保不与内置事件冲突。因此,在代码中应该定义类似enum { LV_EVENT_MY_CUSTOM = LV_EVENT_LAST + 1 }。 另外,发送事件时,可能需要传递参数,这需要使用lv_event_set_param和lv_event_get_param函数。需要确保参数的生命周期有效,避免悬空指针。 总结回答的结构应该是: 1. 创建对象并注册事件回调。 2. 定义自定义事件代码。 3. 在某个事件(如点击)触发时发送自定义事件4. 在另一个对象的回调中处理自定义事件。 5. 解释代码的关键部分和注意事项。 最后,生成相关问题,帮助用户深入理解,比如如何传递数据、事件冒泡的影响、多个自定义事件的处理等。</think>### LVGL发送自定义事件实现方法 在LVGL中实现自定义事件需要三个步骤:创建事件回调函数、发送自定义事件、绑定事件处理逻辑。以下示例展示如何通过按钮点击触发自定义事件: ```c // 定义自定义事件代码(必须大于LV_EVENT_LAST) enum { LV_EVENT_MY_CUSTOM = LV_EVENT_LAST + 1 }; // 父容器的事件处理函数 static void parent_event_cb(lv_event_t * e) { if(lv_event_get_code(e) == LV_EVENT_MY_CUSTOM) { const char* msg = lv_event_get_param(e); printf("收到自定义事件: %s\n", msg); } } // 按钮点击事件处理函数 static void btn_event_cb(lv_event_t * e) { if(lv_event_get_code(e) == LV_EVENT_CLICKED) { // 发送自定义事件到父容器 char* data = "紧急状态"; lv_event_send(lv_obj_get_parent(e->target), LV_EVENT_MY_CUSTOM, data); } } void create_ui(void) { // 创建父容器 lv_obj_t * parent = lv_obj_create(lv_scr_act()); lv_obj_add_event_cb(parent, parent_event_cb, LV_EVENT_MY_CUSTOM, NULL); // 创建按钮 lv_obj_t * btn = lv_btn_create(parent); lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); } ``` 代码关键说明: 1. 自定义事件代码必须从$LV_EVENT_LAST+1$开始定义,避免与系统事件冲突[^2] 2. `lv_event_send`参数说明: - 第一个参数:接收事件对象(示例中使用父容器) - 第二个参数:事件代码$LV_EVENT_MY_CUSTOM$ - 第三个参数:传递数据的指针(支持任意类型数据) 3. 事件数据生命周期需保持有效,建议使用全局/静态变量或动态内存分配
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值