multswipe控件

1. 使用场景

multswipe控件是基于lvgl框架扩展的滑动操作控件,主要为multlist列表项(lv_multlist_item_t)提供滑动交互能力,支持不同样式的滑动操作(如基础滑动、带删除按钮滑动、带扩展图标滑动),核心应用于列表项的滑动删除、滑动触发自定义操作等场景,例如:

  • 列表项滑动超过阈值自动触发删除/自定义处理逻辑;

  • 列表项左滑/上滑露出删除按钮,点击按钮删除列表项;

  • 聚焦状态下的列表项滑动权限控制(仅对齐时允许滑动删除)。

2. 接口介绍

2.1 核心数据类型

接口函数

功能说明

参数说明

lv_multswipe_enable

启用列表项的拖拽删除功能,为列表项内容添加滑动覆盖层

item:multlist的列表项对象;content:需要添加覆盖层的列表项内容对象;style:滑动样式(LV_MULTSWIPE_STYLE_DEFAULT/LV_MULTSWIPE_STYLE_WITH_BTN/LV_MULTSWIPE_STYLE_WITH_EXPAN);返回值:滑动覆盖层对象

lv_multswipe_set_process_cb

设置拖拽过程的回调函数,用于判断滑动边缘作用的列表项

item:multlist的列表项对象;process:拖拽过程的回调函数(process_cb类型,入参包含列表项、内容、删除按钮、处理标识);返回值:无

lv_multswipe_set_del_btn

为列表项设置删除按钮(仅对LV_MULTSWIPE_STYLE_WITH_BTN样式生效)

item:multlist的列表项对象;del_btn:点击触发删除的按钮对象;返回值:无

lv_multswipe_set_src

为列表项设置删除图标资源(仅对LV_MULTSWIPE_STYLE_WITH_EXPAN样式生效)

item:multlist的列表项对象;del_src:图片资源的指针;返回值:无

lv_multswipe_enbale_focus

设置仅当列表项处于聚焦对齐状态时,启用滑动删除功能

item:multlist的列表项对象;en:启用/禁用标识(1启用,0禁用);返回值:无

lv_multswipe_set_thres

设置滑动阈值,列表项聚焦或拖拽超过该范围时触发删除

item:multlist的列表项对象;thres_val:阈值数值;返回值:无

lv_multswipe_set_springback

设置列表项可拖拽的最大范围(回弹阈值)

item:multlist的列表项对象;springback:最大拖拽距离;返回值:无

lv_multswipe_set_dir

设置列表项的拖拽方向

item:multlist的列表项对象;dir:拖拽方向(1为正方向,0为负方向);返回值:无

3. 使用案例

3.1 默认上滑/左滑删除案例

当需要使能默认删除方式时,只需再multlist的新建item回调函数中调用lv_multswipe_enable接口即可使能左滑或上滑删除。

lv_obj_t *tlv_edit_create_item(lv_obj_t *parent, lv_multlist_item_t *item)
{
    tlv_edit_t *edit = (tlv_edit_t *)lv_obj_get_user_data(parent);

    lv_obj_t *item_cont = lv_obj_create(parent);
    lv_obj_remove_style_all(item_cont);
    lv_obj_set_size(item_cont, item->org_w, item->org_h);
    lv_obj_set_style_bg_opa(item_cont, LV_OPA_100, LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(item_cont, LV_COLOR_BLACK, LV_STATE_DEFAULT);
    lv_obj_add_flag(item_cont, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_EVENT_BUBBLE);
    lv_obj_clear_flag(item_cont, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_refr_size(item_cont);

    lv_obj_t *item_btn = lv_obj_create(item_cont);
    lv_obj_remove_style_all(item_btn);
    lv_obj_set_size(item_btn, item->org_w, item->org_h);
    lv_obj_set_style_radius(item_btn, 20, LV_STATE_DEFAULT);
    lv_obj_set_style_border_width(item_btn, 4, LV_STATE_DEFAULT);
    lv_obj_set_style_border_opa(item_btn, LV_OPA_100, LV_STATE_DEFAULT);
    lv_obj_set_style_border_color(item_btn, lv_color_make(0x55, 0x55, 0x55), LV_STATE_DEFAULT);
    lv_obj_add_flag(item_btn, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_EVENT_BUBBLE);
    lv_obj_clear_flag(item_btn, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_refr_size(item_btn);
    const void *img_src = APP_GET_IMG(tlv_edit_add);
    if (item->info)
    {
        tlv_node_t *node = (tlv_node_t *)item->info;
        if ((TLV_ADD_TYPE == edit->type && node->installed) || \
                edit->cur_node && (0 == strcmp(edit->cur_node, node->desc.id_str)))
            lv_obj_set_style_border_color(item_btn, lv_color_make(0xFF, 0, 0), LV_STATE_DEFAULT);

        img_src = node->desc.thumbnail;
		//当需要使能默认删除方式时,只需调用lv_multswipe_enable接口使能即可
        if (rt_strcmp(node->desc.id_str, "tlv_shortcut") && TLV_ADD_TYPE != edit->type)
            item_cont = lv_multswipe_enable(item, item_cont, LV_MULTSWIPE_STYLE_DEFAULT);
    }

    if (img_src)
    {
        lv_obj_t *img = lv_img_create(item_btn);
        lv_img_set_src(img, img_src);
        lv_obj_refr_size(img);
        lv_coord_t zoom_w = (item->org_w - 20) * LV_IMG_ZOOM_NONE / lv_obj_get_width(img);
        lv_coord_t zoom_h = (item->org_w - 20) * LV_IMG_ZOOM_NONE / lv_obj_get_height(img);
        lv_img_set_zoom(img, LV_MIN(zoom_w, zoom_h));
        lv_obj_add_flag(img, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_EVENT_BUBBLE);
        lv_obj_center(img);
    }
    return item_cont;
}


//注册删除事件回调,用于内存释放、对齐等操作
void tlv_edit_envent_cb(lv_event_t *e)
{
	//.......
	//去掉无关代码
    if (LV_EVENT_SWIPE_DELETE == code)
    {
        lv_multlist_item_t *item = lv_event_get_param(e);
        if (item && item->info)
        {
            tlv_node_t *tlv_node = (tlv_node_t *)item->info;
            tlv_node->installed = false;
            //rt_list_remove(&tlv_node->list);
        }
        if (item && item->snapshot)
        {
            lv_obj_del(item->snapshot);
            item->snapshot = NULL;
        }
        if ((uint32_t)(item->index + 1) >= lv_multlist_get_info_cnt(multlist))
            lv_multlist_focus_near(multlist, 0, false, true);
        item = lv_multlist_get_item_by_index(multlist, 0);
        if (!item)
        {
            gui_app_goback();
            return;
        }
    }
}

案例效果展示

multswipe滑动删除效果

3.2 带删除图标样式


//multlist元素创建回调接口中调用lv_multswipe_enable,并设置删除回调函数,用于删除图标的创建

static lv_obj_t *sport_create_item_cb(lv_obj_t *parent, lv_multlist_item_t *item)
{
    uint32_t item_type = (uint32_t)item->user_data;

    lv_obj_t *item_btn = lv_multobj_create(parent);
    lv_obj_remove_style_all(item_btn);
    lv_obj_set_size(item_btn, item->org_w, item->org_h);
    lv_obj_set_style_radius(item_btn, 12, LV_PART_MAIN);
    lv_obj_set_style_bg_opa(item_btn, 255, LV_STATE_DEFAULT);
    lv_obj_add_flag(item_btn, LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_EVENT_BUBBLE);
    lv_obj_clear_flag(item_btn, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_refr_size(item_btn);

    if (SPORT_TYPE_ADD_BTN == item_type)
    {
        lv_obj_set_style_bg_color(item_btn, lv_color_make(0x52, 0x52, 0x48), LV_STATE_DEFAULT);
        lv_obj_set_style_radius(item_btn, lv_obj_get_height(item_btn) >> 1, LV_PART_MAIN);
        lv_obj_t *add_txt = lv_label_create(item_btn);
        lv_ext_set_local_font(add_txt, FONT_TITLE, LV_COLOR_WHITE);
        lv_label_set_text(add_txt, app_get_str(key_add_sport, "Add sport"));
        lv_obj_align_to(add_txt, item_btn, LV_ALIGN_CENTER, 0, 0);
        lv_obj_add_event_cb(item_btn, sport_add_btn_click_cb, LV_EVENT_SHORT_CLICKED, NULL);
    }
    else
    {
        sport_element_t *sport_item = (sport_element_t *)item->info;

        lv_obj_set_style_bg_color(item_btn, lv_color_make(0x4E, 0x66, 0x1A), LV_STATE_DEFAULT);
        lv_obj_set_style_radius(item_btn, BTN_BG_CORNER, LV_PART_MAIN);

        lv_obj_t *sport_icon = lv_img_create(item_btn);
        lv_img_set_src(sport_icon, sport_item->src);
        lv_obj_refr_size(sport_icon);
        lv_obj_refr_pos(sport_icon);
        lv_obj_align(sport_icon, LV_ALIGN_TOP_LEFT, 0, 0);

        lv_obj_t *sport_name = lv_label_create(item_btn);
        lv_ext_set_local_font(sport_name, FONT_TITLE, LV_COLOR_WHITE);
        lv_label_set_text(sport_name, app_get_str_from_id(sport_item->txt));
        lv_obj_align(sport_name, LV_ALIGN_TOP_LEFT, 10, lv_obj_get_y(sport_icon) + lv_obj_get_height(sport_icon));
        lv_obj_refr_size(sport_name);
        lv_obj_refr_pos(sport_name);

        lv_obj_t *train_type = lv_label_create(item_btn);
        lv_ext_set_local_font(train_type, FONT_SUBTITLE, LV_COLOR_WHITE);
        lv_label_set_text(train_type, app_get_str(key_free_train, "Free training"));
        lv_obj_refr_size(train_type);
        lv_obj_align(train_type, LV_ALIGN_TOP_LEFT, 10, lv_obj_get_y(sport_name) + lv_obj_get_height(sport_name));

        lv_obj_t *icon = lv_img_create(item_btn);
        lv_img_set_src(icon, APP_GET_IMG(img_sport_setting));
        lv_obj_refr_size(icon);
        lv_obj_align(icon, LV_ALIGN_TOP_RIGHT, -10, 10);
        lv_obj_add_flag(icon, LV_OBJ_FLAG_CLICKABLE);
        lv_obj_add_event_cb(icon, sport_item_more_click_cb, LV_EVENT_SHORT_CLICKED, (void *)sport_item);

        lv_obj_add_event_cb(item_btn, sport_item_click_cb, LV_EVENT_SHORT_CLICKED, (void *)sport_item);

        lv_multswipe_set_process_cb(item, sport_item_del_proc);
        item_btn = lv_multswipe_enable(item, item_btn, LV_MULTSWIPE_STYLE_WITH_BTN);
    }
    return item_btn;
}


//删除滑动回调函数
void sport_item_del_proc(lv_multlist_item_t *item, lv_obj_t *content, lv_obj_t *del_btn, int32_t offset)
{
    if (NULL == del_btn)
    {
        lv_coord_t content_h = lv_obj_get_height(content);
        del_btn = lv_obj_create(item->element);
        lv_obj_remove_style_all(del_btn);
        lv_obj_set_size(del_btn, SPORT_DEL_BTN_W, content_h);
        lv_obj_set_style_radius(del_btn, BTN_BG_CORNER, LV_STATE_DEFAULT);
        lv_obj_set_style_bg_color(del_btn, lv_color_make(0x4E, 0x66, 0x1A), LV_STATE_DEFAULT);
        lv_obj_set_style_bg_opa(del_btn, 255, LV_STATE_DEFAULT);
        lv_obj_add_flag(del_btn, LV_OBJ_FLAG_CHECKABLE | LV_OBJ_FLAG_EVENT_BUBBLE);
        lv_obj_clear_flag(del_btn, LV_OBJ_FLAG_SCROLLABLE);
        lv_obj_move_background(del_btn);
        lv_obj_set_user_data(del_btn, (void *)item);
        lv_obj_add_event_cb(del_btn, sport_del_item, LV_EVENT_SHORT_CLICKED, (void *)item);

        lv_obj_t *del_img = lv_img_create(del_btn);
        lv_img_set_src(del_img, APP_GET_IMG(img_sport_del));
        lv_obj_clear_flag(del_img, LV_OBJ_FLAG_SCROLLABLE);
        lv_obj_center(del_img);
        lv_obj_refr_size(del_btn);
        lv_multswipe_set_thres(item, SPORT_DEL_BTN_W + 20);
        lv_multswipe_set_springback(item, SPORT_DEL_BTN_W + 10);
        lv_multswipe_set_del_btn(item, del_btn);
    }
    uint16_t offset_bas = LV_ABS(offset);
    if (offset_bas > SPORT_DEL_BTN_W + 10)
        lv_obj_set_width(del_btn, offset_bas - 10);
    else
        lv_obj_set_width(del_btn, SPORT_DEL_BTN_W);

    lv_obj_set_x(del_btn, LV_HOR_RES_MAX - offset_bas + 10);
    lv_coord_t opa = lv_map(LV_ABS(offset), 0, LV_HOR_RES_MAX >> 2, 0, 255);
    lv_obj_set_style_bg_opa(del_btn, opa, 0);
    lv_obj_t *img = lv_obj_get_child(del_btn, 0);
    if (img)
    {
        lv_obj_set_style_img_opa(img, opa, 0);
        lv_obj_center(img);
    }
}

案例效果展示

multswipe滑动删除效果

3.3 消息删除样式

//multlist元素创建回调接口中调用lv_multswipe_enable,并设置删除回调函数,用于滑动过程的处理
static lv_obj_t *notify_create_item_cb(lv_obj_t *parent, lv_multlist_item_t *item)
{
    lv_obj_t *element = NULL;
    uint32_t item_type = (uint32_t)item->info;
    if (ELEMENT_TYPE_CLEAR_ALL == item_type)
    {
        element = lv_obj_create(parent);
        lv_obj_set_size(element, item->org_w, item->org_h);
        lv_obj_set_style_radius(element, 20, 0);
        lv_ext_set_local_bg(element, lv_color_hex(0x353535), LV_OPA_100);
        lv_obj_add_event_cb(element, notify_del_all_message, LV_EVENT_SHORT_CLICKED, NULL);

        lv_obj_t *clear_btn_txt = lv_label_create(element);
        lv_ext_set_local_font(clear_btn_txt, FONT_TITLE, LV_COLOR_WHITE);
        lv_label_set_text(clear_btn_txt, "Clean all");

        lv_obj_align_to(clear_btn_txt, element, LV_ALIGN_CENTER, 0, 0);
    }
    if (ELEMENT_TYPE_CONTENT == item_type)
    {
        message_node_t *msg = (message_node_t *)item->user_data;
        uint16_t msg_cnt = notify_get_type_message_cnt(msg->type);
        element = notify_create_preview(parent, msg, ELEMENT_CONTENT_WIDTH, msg_cnt);
        lv_obj_add_event_cb(element, notify_message_details_cb, LV_EVENT_SHORT_CLICKED, (void *)item);
        lv_multswipe_set_process_cb(item, notify_draw_del_proc);
        element = lv_multswipe_enable(item, element, LV_MULTSWIPE_STYLE_WITH_BTN);
    }
    if (ELEMENT_TYPE_NO_MESSAGE == item_type)
    {
        element = lv_obj_create(parent);
        lv_obj_remove_style_all(element);
        lv_obj_set_size(element, item->org_w, item->org_h);
        //lv_ext_set_local_bg(element, LV_COLOR_GRAY, LV_OPA_30);
        lv_obj_center(element);

        lv_obj_t *txt_lab = lv_label_create(element);
        lv_ext_set_local_font(txt_lab, FONT_TITLE, LV_COLOR_WHITE);
        lv_label_set_text(txt_lab, "No message.");
        lv_obj_center(txt_lab);
    }
    return element;
}

//滑动过程回调函数
void notify_draw_del_proc(lv_multlist_item_t *item, lv_obj_t *content, lv_obj_t *del_btn, int32_t offset)
{
    if (NULL == del_btn)
    {
        lv_coord_t content_h = lv_obj_get_style_height(content, 0);
        del_btn = lv_obj_create(item->element);
        lv_obj_remove_style_all(del_btn);
        if (0 == lv_obj_get_style_bg_opa(content, 0))
            lv_obj_set_size(del_btn, 10, content_h - 27);
        else
            lv_obj_set_size(del_btn, 10, content_h);
        lv_obj_set_style_radius(del_btn, 20, LV_STATE_DEFAULT);
        lv_obj_set_style_border_width(del_btn, 0, LV_STATE_DEFAULT);
        lv_obj_set_style_bg_color(del_btn, lv_color_hex(0x353535), LV_STATE_DEFAULT);
        lv_obj_set_style_bg_opa(del_btn, LV_OPA_100, LV_STATE_DEFAULT);
        lv_obj_add_flag(del_btn, LV_OBJ_FLAG_CHECKABLE);
        lv_obj_clear_flag(del_btn, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_EVENT_BUBBLE);
        lv_obj_set_user_data(del_btn, (void *)item);
        lv_obj_add_event_cb(del_btn, notify_del_one_item, LV_EVENT_SHORT_CLICKED, (void *)item);

        lv_obj_t *del_img = lv_img_create(del_btn);
        lv_img_set_src(del_img, APP_GET_IMG(img_delete_gray));
        lv_obj_clear_flag(del_img, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_EVENT_BUBBLE);
        lv_obj_center(del_img);

        lv_multswipe_set_del_btn(item, del_btn);
    }
    lv_obj_t *del_img = lv_obj_get_child(del_btn, 0);
    lv_obj_align(content, LV_ALIGN_CENTER, offset, 0);
#define DEL_BTN_GAP     10
#define DEL_BTN_W_DEF   100
#define DEL_BTN_W_MAX   (LV_HOR_RES_MAX>>1)
    /*The farthest sliding distance*/
    if (offset + DEL_BTN_W_MAX + DEL_BTN_GAP < 0)
    {
        lv_obj_set_width(del_btn, DEL_BTN_W_MAX);
        lv_obj_align_to(del_btn, content, LV_ALIGN_OUT_RIGHT_BOTTOM, DEL_BTN_GAP, 0);
        if (del_img)
            lv_obj_align(del_img, LV_ALIGN_CENTER, 0, 0);
    }
    /*Jitter initiation stage*/
    else if (offset + DEL_BTN_W_DEF + DEL_BTN_GAP < 0)
    {
        uint16_t btn_w = -DEL_BTN_GAP - offset;
        lv_obj_set_width(del_btn, btn_w);
        lv_obj_align_to(del_btn, content, LV_ALIGN_OUT_RIGHT_BOTTOM, DEL_BTN_GAP, 0);
        if (del_img)
        {
            lv_img_set_zoom(del_img, LV_IMG_ZOOM_NONE);
            lv_obj_align(del_img, LV_ALIGN_CENTER, 0, 0);
        }
    }
    else
    {
        lv_obj_set_width(del_btn, DEL_BTN_W_DEF);
        lv_obj_align_to(del_btn, content, LV_ALIGN_OUT_RIGHT_BOTTOM, DEL_BTN_GAP, 0);
        lv_coord_t zoom = -offset - DEL_BTN_GAP;
        zoom = zoom < DEL_BTN_GAP ? 0 : zoom;
        zoom = zoom * LV_IMG_ZOOM_NONE / DEL_BTN_W_DEF;
        if (del_img)
        {
            lv_img_set_zoom(del_img, zoom);
            lv_obj_align(del_img, LV_ALIGN_CENTER, 0, 0);
        }
    }
}

案例效果展示

multswipe滑动删除效果

4. 注意事项

  1. 样式匹配:

    • lv_multswipe_set_del_btn仅对LV_MULTSWIPE_STYLE_WITH_BTN样式生效;

    • lv_multswipe_set_src仅对LV_MULTSWIPE_STYLE_WITH_EXPAN样式生效;

    • 非对应样式调用上述接口无效果。

  2. 依赖关系:控件依赖lvsf_multlist.h,使用前需确保该头文件已包含且LVSF_USE_MULTSIWPE宏定义不为0。

  3. 聚焦控制:lv_multswipe_enbale_focus仅在列表项对齐状态下生效,非对齐状态无法启用滑动删除。

  4. 回调函数:process_cb回调需自行实现滑动阈值判断、删除逻辑等业务,框架仅提供回调触发能力。

  5. 方向控制:滑动方向(正/负)需结合实际UI布局定义,建议先测试方向参数对滑动效果的影响。

  6. 资源释放:删除列表项时,需确保滑动控件相关对象(如删除按钮、覆盖层)同步释放,避免内存泄漏。