LVGL v8.1.0 lv_table 内存泄漏问题

本文针对LVGL中lv_table组件释放后存在的内存泄漏问题进行了详细分析,并提供了有效的解决方案。问题出现在lv_table_destructor函数中,由于未释放table->col_w导致内存泄漏。

问题

最近在用LVGL做GUI,期间遇上 lv_table 释放后存在内存泄漏问题

偶然看到github有人也遇上此问题并提出了解决方案,经测有效 HiFeiz

HiFeiz 原创文章

经 @lovemengx 提示, LVGL v8.2.0 已经修复此问题


目录

问题

文件路径

问题函数

解决方案


文件路径 HiFeiz

lvgl\src\widgets\lv_table.c

问题函数 HiFeiz

static void lv_table_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj){
    LV_UNUSED(class_p);
    lv_table_t* table = (lv_table_t*)obj;
    /*Free the cell texts @ HiFeiz*/
    uint16_t i;
    for (i = 0; i < table->col_cnt * table->row_cnt; i++) {
        if (table->cell_data[i]) {
            lv_mem_free(table->cell_data[i]);
            table->cell_data[i] = NULL;
        }
    }
    if (table->cell_data) lv_mem_free(table->cell_data);
    if (table->row_h) lv_mem_free(table->row_h);
}

释放了 table->row_h 却未释放 table->col_w ,所以出现内存泄漏

解决方案  @ HiFeiz

static void lv_table_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj){
    LV_UNUSED(class_p);
    lv_table_t* table = (lv_table_t*)obj;
    /*Free the cell texts @ HiFeiz*/
    uint16_t i;
    for (i = 0; i < table->col_cnt * table->row_cnt; i++) {
        if (table->cell_data[i]) {
            lv_mem_free(table->cell_data[i]);
            table->cell_data[i] = NULL;
        }
    }
    if (table->cell_data) lv_mem_free(table->cell_data);
    if (table->row_h) lv_mem_free(table->row_h);
    if (table->col_w) lv_mem_free(table->col_w); //增加此行  @ HiFeiz
}

成功解决

<think>我们正在处理关于LVGL问题,特别是如何取消由`lv_obj_set_flex_flow`引发的对焦滚动行为。根据引用内容,我们知道LVGL的基础对象(lv_obj)具有事件处理的能力(Events)。因此,我们可以通过事件处理来解决这个问题。分析:当使用`lv_obj_set_flex_flow`设置Flex布局时,LVGL可能会自动启用滚动,并且在焦点移动时自动滚动以显示被聚焦的对象。要取消这种滚动行为,我们需要干预焦点变化时的滚动动作。解决方案思路:1.禁用对象的滚动:我们可以尝试使用`lv_obj_clear_flag(obj,LV_OBJ_FLAG_SCROLLABLE)`来禁用滚动。但是,这可能会完全禁用滚动,而不仅仅是焦点滚动。2.事件处理:我们可以为对象添加事件回调,在焦点变化的事件中阻止滚动行为。具体来说,我们可以监听`LV_EVENT_FOCUSED`事件,并在事件回调中执行一些操作来阻止自动滚动。然而,根据LVGL的文档,焦点变化时的自动滚动是由`lv_obj_scroll_to_view`函数触发的。我们可以通过事件回调来阻止这个行为。具体步骤:-为需要取消焦点滚动的对象(或者其父对象)添加事件回调。-在事件回调中,当事件为`LV_EVENT_FOCUSED`时,我们可以阻止默认的滚动行为。但是注意,`LV_EVENT_FOCUSED`事件本身并不直接提供阻止滚动的方法,因为滚动行为可能是由系统在焦点事件后触发的。-另一种方法是监听`LV_EVENT_SCROLL`事件并阻止它,但这可能不是由焦点变化触发的滚动。更准确的做法是:在焦点事件中,我们暂时禁用滚动,然后再启用?这样可能不行,因为滚动可能发生在焦点事件之后。经过查阅资料(虽然没有直接引用,但根据经验),我们可以使用以下方法:在对象的事件回调中,当收到`LV_EVENT_FOCUSED`事件时,我们可以调用`lv_obj_clear_state(obj,LV_STATE_FOCUSED)`来清除焦点状态?这显然不行,因为这样焦点就没了。另一种方法:我们可以设置对象的一个标志,告诉它不要滚动。但是LVGL没有直接提供这样的接口。实际上,在LVGL中,焦点变化后自动滚动的行为是由`lv_group_focus_obj`函数内部调用的`lv_obj_scroll_to_view`函数完成的。如果我们不想让某个对象在获得焦点时自动滚动,我们可以通过设置该对象的某个属性来避免。在LVGLv8中,我们可以使用`lv_obj_set_scroll_snap_x`和`lv_obj_set_scroll_snap_y`吗?不,这不对。实际上,在LVGLv8中,有一个API可以控制是否在聚焦时滚动到视图:`lv_obj_set_scroll_to_focus`。但是,在LVGLv8中,这个API可能不存在。在较新的版本(如v8)中,我们可以通过设置对象的属性来控制。然而,根据问题中提到的引用,没有直接提到这个函数。因此,我们可以考虑另一种方法:在焦点事件中阻止滚动。在事件回调中,我们可以这样做:1.监听`LV_EVENT_FOCUSED`事件。2.在事件回调中,我们手动将滚动位置重置为当前的位置(即不改变滚动位置),或者直接停止任何滚动动画。但是,这并不能阻止LVGL在焦点事件后自动执行滚动。我们需要在滚动发生之前阻止它。经过思考,我们可以通过设置对象为不可滚动(但这样会完全禁用滚动)?或者我们可以使用事件回调来捕获即将发生的滚动并取消它。实际上,在LVGL中,我们可以通过事件冒泡机制来阻止默认行为。但是,`lv_obj_scroll_to_view`函数是直接调用的,并没有事件。因此,我们需要在对象获得焦点时立即阻止滚动到视图。我们可以通过覆盖默认的焦点处理行为来实现。在LVGL中,有一个函数`lv_obj_set_scroll_to_focus`(在LVGLv8.1.0及以上)可以控制是否在聚焦时滚动到视图。但是,如果使用的是较早版本,则没有这个函数。假设我们使用的是较新版本(如v8.1.0及以上),我们可以这样禁用滚动到焦点对象:`lv_obj_set_scroll_to_focus(obj,false);`但是,如果我们的版本没有这个函数,我们可以采用事件回调的方式,在焦点事件中阻止后续的滚动操作。然而,在LVGLv8中,我们可以尝试以下方法:在对象的事件回调中:```cstaticvoidevent_cb(lv_event_t*e){lv_event_code_tcode=lv_event_get_code(e);if(code==LV_EVENT_FOCUSED){//阻止自动滚动:通过设置一个标志告诉LVGL不要滚动,但LVGL没有提供这样的标志。//因此,我们可以立即将滚动位置重置为当前位置,或者停止滚动动画。lv_obj_t*obj=lv_event_get_target(e);lv_obj_scroll_to(obj,0,0,LV_ANIM_OFF);//这样会立即停止滚动并重置位置?但这不是我们想要的,因为我们不想改变当前位置。//或者,我们可以什么都不做,但这样默认的滚动行为还是会执行。}}```但是,上述方法并不能阻止LVGL内部调用`lv_obj_scroll_to_view`。因为`lv_obj_scroll_to_view`会在焦点事件之后被调用。所以,我们需要在LVGL调用`lv_obj_scroll_to_view`之前阻止它。由于`lv_obj_scroll_to_view`是公开函数,我们无法直接阻止,但我们可以通过修改组(group)的行为来实现。在LVGL中,焦点移动是通过组(group)来管理的。我们可以自定义组的焦点回调函数(focus_cb),在默认的焦点回调中,会调用`lv_obj_scroll_to_view`。因此,我们可以设置自己的焦点回调,并在其中选择是否滚动。步骤:1.获取对象所在的组(group)。2.设置组的焦点回调函数(focus_cb)为我们自定义的函数。3.在自定义的焦点回调函数中,我们判断是否需要滚动,如果不需要,就不调用`lv_obj_scroll_to_view`。示例代码:```c//自定义组的焦点回调staticvoidmy_focus_cb(lv_group_t*group){lv_obj_t*focused=lv_group_get_focused(group);//这里可以添加条件判断,例如根据对象的某个自定义属性来决定是否滚动//如果不需要滚动,就什么也不做;否则调用默认的滚动到视图函数。//但是,我们可能希望保留其他对象的滚动行为,只对特定对象禁用。//假设我们有一个标记,表示该对象不需要滚动到视图if(lv_obj_has_flag(focused,MY_FLAG_DONT_SCROLL_ON_FOCUS)){//什么都不做}else{//调用默认的滚动行为lv_obj_scroll_to_view(focused,LV_ANIM_OFF);//注意:默认情况下,LVGL在滚动时可能有动画,这里我们使用LV_ANIM_OFF立即滚动,但也可以使用其他动画//实际上,默认的焦点回调中调用的是`lv_obj_scroll_to_view(focused,LV_ANIM_ON);`}}//然后,在初始化组时,或者修改组的回调:lv_group_t*group=lv_group_get_default();//或者获取对象所在的组lv_group_set_focus_cb(group,my_focus_cb);```但是,这种方法需要修改组的行为,可能会影响组中的其他对象。因此,我们可以为特定组设置回调,或者为特定对象设置一个标志(如上面的`MY_FLAG_DONT_SCROLL_ON_FOCUS`),然后在回调中根据该标志判断。注意:`MY_FLAG_DONT_SCROLL_ON_FOCUS`是一个自定义的标志,我们可以使用`lv_obj`的预留用户数据(user_data)或者使用扩展属性(通过`lv_obj_set_user_data`)来存储这个标志。但是,更简单的方法是使用`lv_obj`的预留标志位(如果有的话)。在LVGL中,每个对象都有一些用户可用的标志位(从`LV_OBJ_FLAG_USER_1`开始)。我们可以这样:```c//定义自己的标志(确保不与其他标志冲突)#defineMY_FLAG_DONT_SCROLL_ON_FOCUSLV_OBJ_FLAG_USER_1//在需要禁用焦点滚动的对象上设置这个标志lv_obj_add_flag(obj,MY_FLAG_DONT_SCROLL_ON_FOCUS);//在自定义焦点回调中:if(lv_obj_has_flag(focused,MY_FLAG_DONT_SCROLL_ON_FOCUS)){//跳过滚动}else{lv_obj_scroll_to_view(focused,LV_ANIM_ON);//或者使用默认的动画设置}```但是,这种方法需要修改组的默认焦点回调,并且要确保所有需要此功能的对象所在的组都使用了这个自定义回调。如果我们不想修改组的回调,或者只希望针对某个特定对象,我们可以尝试在对象的事件回调中,在`LV_EVENT_FOCUSED`事件之后立即滚动回原来的位置?这样体验不好。结论:根据LVGL版本的不同,解决方案也不同:1.如果版本>=v8.1.0,可以直接使用`lv_obj_set_scroll_to_focus(obj,false);`来禁用该对象的焦点滚动。2.对于较低版本,我们可以通过自定义组的焦点回调,并利用对象的自定义标志来控制是否滚动。由于问题中没有提到LVGL版本,我们将提供第二种方法的实现步骤。步骤详解:1.定义一个自定义标志:```c#defineMY_OBJ_FLAG_NO_FOCUS_SCROLLLV_OBJ_FLAG_USER_1```2.为需要禁用焦点滚动的对象添加此标志:```clv_obj_add_flag(obj,MY_OBJ_FLAG_NO_FOCUS_SCROLL);```3.创建自定义的组焦点回调函数:```cstaticvoidmy_group_focus_cb(lv_group_t*group){lv_obj_t*obj=lv_group_get_focused(group);if(!lv_obj_has_flag(obj,MY_OBJ_FLAG_NO_FOCUS_SCROLL)){//执行默认的滚动到视图lv_obj_scroll_to_view(obj,LV_ANIM_ON);}}```4.将自定义焦点回调设置到组中(例如默认组):```clv_group_t*group=lv_group_get_default();lv_group_set_focus_cb(group,my_group_focus_cb);```注意:如果已经存在组的焦点回调,我们需要在自定义回调中调用原来的回调(如果必要的话)。但是,默认的焦点回调就是执行`lv_obj_scroll_to_view`,所以我们这里覆盖了它。如果之前已经设置了焦点回调,我们可以保存原来的回调并在适当的时候调用它。示例(处理原有回调):```cstaticlv_group_focus_cb_toriginal_focus_cb=NULL;staticvoidmy_group_focus_cb(lv_group_t*group){lv_obj_t*obj=lv_group_get_focused(group);if(lv_obj_has_flag(obj,MY_OBJ_FLAG_NO_FOCUS_SCROLL)){//跳过滚动}else{//调用原来的回调if(original_focus_cb)original_focus_cb(group);}}//设置回调时保存原来的回调lv_group_t*group=lv_group_get_default();original_focus_cb=lv_group_get_focus_cb(group);lv_group_set_focus_cb(group,my_group_focus_cb);```这样,我们只修改了默认组的行为。如果对象在其他组中,需要为每个组设置。总结:通过自定义组的焦点回调函数,我们可以有选择地禁止某些对象在获得焦点时自动滚动到视图。注意:这种方法需要在应用程序中设置一次组的回调(例如在初始化时)。同时,确保在设置自定义回调之前保存原来的回调,以保持其他默认行为。因此,针对问题“取消由lv_obj_set_flex_flow引发的对焦滚动行为”,我们可以通过上述自定义组焦点回调的方法来实现。最后,我们提供一种替代方法:如果不需要整个组都改变行为,也可以考虑在每次焦点变化时(通过监听`LV_EVENT_FOCUSED`)手动停止滚动动画,但这可能不够可靠。我们推荐使用自定义组焦点回调的方法。注意:以上代码示例是C语言,因为LVGL是用C编写的。相关问题1.如何为LVGL对象添加自定义标志?2.如何在LVGL中创建和管理组(group)?3.如何修改LVGL对象的滚动行为?</think>### 取消 LVGL 中 `lv_obj_set_flex_flow` 引发的对焦滚动行为 在 LVGL 中,使用 `lv_obj_set_flex_flow` 为对象启用弹性布局(Flex)时,系统默认会激活 **自动对焦滚动行为**(即当子对象获得焦点时自动滚动到可视区域)。以下是取消该行为的解决方案: --- #### **方法 1:禁用对象的滚动标志(推荐)** 直接移除对象的 `LV_OBJ_FLAG_SCROLLABLE` 标志,禁止所有滚动行为: ```c lv_obj_clear_flag(parent_obj, LV_OBJ_FLAG_SCROLLABLE); // 禁用滚动能力 ``` **效果**: - 父容器不再响应滚动操作(包括对焦滚动)[^1][^2]。 --- #### **方法 2:拦截对焦事件** 通过事件回调手动阻止滚动行为: ```c static void event_cb(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_FOCUSED) { lv_obj_clear_flag(lv_event_get_target(e), LV_OBJ_FLAG_SCROLL_WITH_ARROW); // 禁用方向键滚动 lv_obj_scroll_to_view(lv_event_get_target(e), LV_ANIM_OFF); // 立即停止滚动动画 } } // 为所有子对象添加事件监听 for (int i = 0; i < lv_obj_get_child_cnt(parent_obj); i++) { lv_obj_t * child = lv_obj_get_child(parent_obj, i); lv_obj_add_event_cb(child, event_cb, LV_EVENT_FOCUSED, NULL); } ``` **效果**: - 子对象获得焦点时不会触发滚动动画[^1][^3]。 --- #### **方法 3:自定义滚动策略** 通过覆盖默认滚动行为: ```c static void scroll_event_cb(lv_event_t * e) { if (lv_event_get_code(e) == LV_EVENT_SCROLL) { lv_event_stop_bubbling(e); // 阻止事件冒泡 } } lv_obj_add_event_cb(parent_obj, scroll_event_cb, LV_EVENT_SCROLL, NULL); ``` **效果**: - 父容器拦截所有滚动事件,包括对焦触发的滚动[^2]。 --- ### **关键概念说明** 1. **滚动标志** - `LV_OBJ_FLAG_SCROLLABLE`:启用/禁用对象滚动能力。 - `LV_OBJ_FLAG_SCROLL_WITH_ARROW`:控制方向键是否触发滚动。 2. **事件冒泡** LVGL 事件从子对象向父对象传递(冒泡),拦截事件可终止默认行为[^3]。 3. **弹性布局与滚动** `lv_obj_set_flex_flow()` 仅定义布局规则,滚动行为由独立标志控制。 > ⚠️ **注意事项**: > - 若需保留手动滚动但禁用对焦滚动,优先使用 **方法 2**。 > - 检查 LVGL 版本(v8.3+ 支持更精细的滚动控制)。 --- ###
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值