试用 LVGL Pro,一套完整的工具包,助您高效构建、测试、分享和交付 UI!
LVGL
教程

在 LVGL Pro 中构建带滚动效果的自定义控件

学习如何使用 XML 定义和 C 回调函数在 LVGL Pro 中构建带滚动平移效果的自定义控件。

费利克斯·比戈费利克斯·比戈8 分钟阅读
在 LVGL Pro 中构建带滚动效果的自定义控件

简介#

LVGL Pro 允许你在编辑器中可视化地设计 UI 组件,但某些效果需要超出样式范围的运行时逻辑。本教程将介绍如何构建一个实现滚动平移效果的自定义控件——列表项根据滚动位置水平偏移,形成类似滚轮的 UI 效果。

你将在 XML 中定义控件结构,导出生成的 C 代码,并连接滚动事件回调函数来驱动视觉效果。

文档滚动滚动平移
Full documentation

滚动平移示例展示了如何在滚动过程中动态重新定位子元素,以创建弧形或滚轮式的视觉效果。


术语说明#

在开始之前,了解 LVGL Pro 如何组织 UI 元素非常重要。

内置控件#

LVGL 包含现成的控件,如 lv_arclv_buttonlv_label

  • 可在编辑器中直接使用
  • 无需自定义实现

组件(Component)#

组件是由一个或多个对象组成的可复用 UI 模块。

  • 完全在编辑器中定义
  • 专注于布局和样式
  • 无需自定义 C 逻辑

控件(Widget)#

控件在组件基础上扩展了用 C 实现的自定义行为

在以下情况使用控件:

  • 仅通过样式无法实现所需行为
  • 需要事件驱动或动态逻辑
提示

滚动平移需要运行时计算,因此非常适合作为自定义控件来实现。


滚动平移效果概述#

滚动平移效果使列表呈现滚轮般的行为:

  • 列表项根据垂直位置水平偏移
  • 靠近中心的项目更为突出
  • 滚动过程中持续更新效果

创建基础控件#

  1. 打开 LVGL Pro
  2. 创建一个名为 wd_list 的新控件
  3. 设计布局并根据需要应用样式

辅助组件:按钮#

滚动列表为每一行使用一个简单的按钮组件。定义此组件以便将其用作控件的子元素:

button.xml
<component>
	<previews>
		<preview width="320" height="240" style_bg_color="0xeee" />
	</previews>
	<api>
		<prop name="label" type="string" default="Label 1" />
	</api>
	<styles>
		<style name="style_base" width="100%" pad_ver="20" radius="40" />
	</styles>
	<view extends="lv_button">
		<style name="style_base" />
		<lv_label text="$label" align="center" />
	</view>
</component>
xml

定义控件 XML#

创建控件结构并暴露一个配置属性。

wd_list.xml
<widget>
	<previews>
		<preview width="320" height="240" style_bg_color="0xeee" />
	</previews>
	<api>
		<prop name="translate_scroll" type="bool" default="false" />
	</api>
	<styles>
		<style
			name="style_base"
			width="100%"
			height="100%"
			pad_all="10"
			pad_row="10"
			layout="flex"
			flex_flow="column"
		/>
	</styles>
	<view extends="lv_obj" scrollbar_mode="off">
		<style name="style_base" />
	</view>
</widget>
xml

translate_scroll 属性允许在不修改实现的情况下启用或禁用该效果。


导出生成的代码#

在 LVGL Pro 中使用 Export Code only 生成控件文件:

my_project
      • wd_list.c在此添加自定义逻辑
      • wd_list.h
      • wd_list_gen.c自动生成,请勿编辑
      • wd_list_gen.h自动生成,请勿编辑
      • wd_list_private_gen.h自动生成,请勿编辑
      • wd_list_xml_parser.c
请勿修改生成的文件

_gen.* 结尾的文件会在导出时被覆盖。请始终在 wd_list.c 中添加自定义逻辑。


实现滚动逻辑#

添加滚动事件回调函数,根据每个子元素与容器中心的距离计算水平平移量:

wd_list.c
static void scroll_event_cb(lv_event_t * e)
{
    lv_obj_t * cont = lv_event_get_target_obj(e);
 
    lv_area_t cont_a;
    lv_obj_get_coords(cont, &cont_a);
    int32_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;
 
    int32_t r = lv_obj_get_height(cont) * 7 / 10;
    int32_t child_cnt = (int32_t)lv_obj_get_child_count(cont);
 
    for(int32_t i = 0; i < child_cnt; i++) {
        lv_obj_t * child = lv_obj_get_child(cont, i);
 
        lv_area_t child_a;
        lv_obj_get_coords(child, &child_a);
 
        int32_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;
        int32_t diff_y = LV_ABS(child_y_center - cont_y_center);
 
        int32_t x;
 
        if(diff_y >= r) {
            x = r;
        } else {
            uint32_t x_sqr = r * r - diff_y * diff_y;
            lv_sqrt_res_t res;
            lv_sqrt(x_sqr, &res, 0x8000);
            x = r - res.i;
        }
 
        lv_obj_set_style_translate_x(child, x, 0);
    }
}
c

绑定事件#

绑定滚动回调函数并配置行为:

wd_list.c
void wd_list_constructor_hook(lv_obj_t *obj)
{
    wd_list_t * widget = (wd_list_t *)obj;
    lv_obj_add_event_cb(obj, scroll_event_cb, LV_EVENT_SCROLL, widget);
    lv_obj_add_event_cb(obj, scroll_event_cb, LV_EVENT_CHILD_CHANGED, widget);
    lv_obj_set_scroll_dir(obj, LV_DIR_VER);
}
c
  • LV_EVENT_SCROLL 在滚动过程中更新位置
  • LV_EVENT_CHILD_CHANGED 在添加或删除项目时更新布局
  • 滚动方向限制为垂直

绑定 API 属性#

translate_scroll XML 属性与 C 实现关联,以便在编辑器中进行切换。

XML 解析器#

wd_list_xml_parser.c
void wd_list_xml_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
    void * item = lv_xml_state_get_item(state);
 
    lv_xml_obj_apply(state, attrs);
 
    for(int i = 0; attrs[i]; i += 2) {
        const char * name = attrs[i];
        const char * value = attrs[i + 1];
            if(lv_streq("translate_scroll", name)) {
                wd_list_set_translate_scroll(item, lv_xml_to_bool(value));
            }
    }
}
c

控件 API#

wd_list.c
void wd_list_set_translate_scroll(lv_obj_t * wd_list, bool translate_scroll)
{
    wd_list_t * widget = (wd_list_t *)wd_list;
    widget->translate_scroll = translate_scroll;
    lv_obj_send_event(wd_list, LV_EVENT_SCROLL, widget);
}
c

更新回调函数#

更新 scroll_event_cb 以响应 translate_scroll 属性:

wd_list.c
static void scroll_event_cb(lv_event_t * e)
{
    lv_obj_t * cont = lv_event_get_target_obj(e);
    wd_list_t * widget = (wd_list_t *)lv_event_get_user_data(e);
 
    /* ... 与之前相同的计算逻辑 ... */
    lv_obj_set_style_translate_x(child, x, 0);
    lv_obj_set_style_translate_x(child, widget->translate_scroll ? x : 0, 0);
    /* ... */
}
c

效果展示#

从 LVGL Pro 导出代码并重新编译项目。该控件现在可以在任何 XML 布局中使用:


总结#

LVGL Pro 中的自定义控件弥合了可视化设计与运行时行为之间的差距。需要记住的关键原则:

  • 组件处理静态布局和样式,在不需要自定义逻辑时使用
  • 控件通过 C 回调函数扩展组件,实现动态行为
  • 将自定义代码放在 wd_list.c 中,切勿修改 _gen.* 文件
  • 通过 XML API 属性暴露可配置的行为,以便在编辑器中进行控制

关于作者

费利克斯·比戈
费利克斯·比戈

LVGL Services 嵌入式界面开发工程师

LVGL Services 的嵌入式界面开发工程师,负责实现客户的 UI 设计,并帮助团队使用 LVGL Pro 构建可用于生产的高质量界面。

认识博客背后的作者们

了解那些分享 LVGL 知识的优秀作者们

查看作者

订阅我们的通讯 不错过任何关于 LVGL 的新闻。我们每月最多发送 2 封邮件。

LVGL

LVGL 是最受欢迎的免费开源嵌入式图形库,支持任何 MCU、MPU 和显示类型,助您构建精美的用户界面。

我们还提供 UI 设计、实现和咨询等服务。

© 2026 LVGL。保留所有权利。
YouTubeGitHubLinkedIn