news 2026/10/21 11:34:39

HoloCubic 项目中的 LVGL Keypad Encoder Demo:无触摸屏下的按键与旋钮交互实现指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HoloCubic 项目中的 LVGL Keypad Encoder Demo:无触摸屏下的按键与旋钮交互实现指南
  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

导读

HoloCubic 是一款带网络功能的伪全息透明显示桌面站,其图形界面基于 LVGL 构建。在开发过程中,硬件交互并不总依赖触摸屏——LVGL 本身支持通过键盘(Keypad)与编码器(Encoder)来操作全部控件,而本仓库LvglSimulator子项目中的lv_demo_keypad_encoder正是这一能力的完整演示:它展示了按钮、复选框、下拉列表、滚动轮、滑块、开关、微调框、列表与文本输入框如何在完全没有触摸设备的情况下被逐项聚焦、选中与编辑。阅读本文后,你将掌握该 Demo 的启用方式、核心交互模型(对象组 Group、导航/编辑双模式、虚拟键盘联动),并能把同一套机制复用到 HoloCubic 嵌入式固件或 PC 模拟器中。

一、Demo 是什么:无触摸交互的完整控件集演示

1.1 官方文档的定位

关联文档 README.md 明确指出:

LVGL allows you to control the widgets with keypad and/or encoder without touchpad. This demo shows how to handle buttons, drop-down lists, rollers, sliders, switches and text inputs without touchpad.

即:LVGL 允许在没有触摸屏的前提下,仅靠键盘和/或编码器控制控件;本 Demo 覆盖按钮、下拉列表、滚动轮、滑块、开关和文本输入等控件的无触摸操作方式。它属于官方 lv_examples 仓库的示例之一,被完整收纳进本仓库的 PC 模拟器工程。

1.2 Demo 的运行截图

下图是该 Demo 在 800×480 模拟器窗口中的实际运行效果,界面顶部是 "Selectors" 与 "Text input" 两个标签页,分别承载各类选择型控件与文本输入控件:

1.3 Demo 覆盖的控件清单

从源码 lv_demo_keypad_encoder.c 的结构体定义可以看到,本 Demo 共演示8 类选择型控件 + 2 个文本输入框:

  • Button(按钮):selector_objs.btn
  • Checkbox(复选框):selector_objs.cb
  • Slider(滑块):selector_objs.slider,范围被设置为 0~10(lv_slider_set_range(slider, 0, 10))
  • Switch(开关):selector_objs.sw
  • Spinbox(微调框):selector_objs.spinbox
  • Dropdown(下拉列表):selector_objs.dropdown
  • Roller(滚动轮):selector_objs.roller
  • List(列表):selector_objs.list,内含 7 个带图标按钮的列表项(Apply、Close、Show、Hide、Delete、Copy、Paste,图标取自LV_SYMBOL_OK、LV_SYMBOL_CLOSE等内置符号)
  • Textarea(文本框):textinput_objs.ta1与textinput_objs.ta2,其中 ta1 设置为单行、隐藏光标、带 "Type something" 占位符

这些控件被组织在lv_tabview(标签视图)的两个标签页中:第一页 "Selectors" 承载前 8 类选择型控件,第二页 "Text input" 承载两个文本框,见 lv_demo_keypad_encoder.c。

二、如何运行这个 Demo

2.1 启用宏:lv_ex_conf.h

按官方文档要求,在 lv_ex_conf.h 中开启:

/*Demonstrate the usage of encoder and keyboard*/ #define LV_USE_DEMO_KEYPAD_AND_ENCODER 1

同时,为了让 Demo 中的键盘与"编码器"驱动真正生效,还需要确保同一配置文件中两个输入设备支持宏也为 1(当前仓库默认均已开启):

#define LV_EX_KEYBOARD 1 /*Add PC keyboard support to some examples (`lv_drivers` repository is required)*/ #define LV_EX_MOUSEWHEEL 1 /*Add 'encoder' (mouse wheel) support to some examples (`lv_drivers` repository is required)*/

对应地,驱动层配置 lv_drv_conf.h 中USE_KEYBOARD与USE_MOUSEWHEEL同样默认为 1,二者相互配合才能把 PC 键盘与鼠标滚轮桥接为 LVGL 输入设备。

2.2 调用入口:lv_demo_keypad_encoder()

官方文档要求:"Afterlv_init()and initializing the drivers calllv_demo_keypad_encoder()"。在模拟器主程序 lvgl_similator.cpp 中,可以看到它在lv_init()与hal_init()之后调用该函数(其余 Demo 均被注释):

lv_init(); hal_init(); //lv_demo_widgets(); //lv_demo_benchmark(); lv_demo_keypad_encoder(); //lv_demo_printer(); //lv_demo_stress();

hal_init()内部完成了显示驱动(monitor_init(),SDL 窗口模拟显示屏)、鼠标指针输入设备、键盘输入设备(USE_KEYBOARD分支下注册keyboard_read)、以及每 5ms 触发一次的lv_tick_inc(5)心跳线程的初始化。Demo 的声明位于 lv_demo_keypad_encoder.h,整体实现包在#if LV_USE_DEMO_KEYPAD_AND_ENCODER条件编译中。

2.3 演示中的操作方式

  • 键盘:方向键在控件间移动焦点;Enter确认/按下;Esc取消;Backspace删除(由 SDL 键盘驱动映射)。
  • 滚轮(模拟编码器):滚动滚轮 = 编码器旋转,用于在控件间切换焦点或调整数值;按下鼠标中键= 编码器按钮(Enter)。
  • 文本输入:聚焦文本框后会自动弹出 LVGL 虚拟键盘(on-screen keyboard),通过键盘/编码器按键进行输入,Apply或Cancel后关闭。

三、核心机制一:对象组(Group)——键盘/编码器导航的基础

3.1 为什么需要 Group

触摸输入靠坐标命中控件,而键盘/编码器没有坐标,只能"按顺序"在控件间移动焦点。LVGL 用Group(对象组)解决这个问题:只有加入某个 Group 的控件,才会被该 Group 关联的输入设备遍历、聚焦和接收按键。

Demo 在 lv_demo_keypad_encoder.c 中创建了全局组g,并把两个输入设备都绑定到它:

g = lv_group_create(); lv_group_set_focus_cb(g, focus_cb); #if LV_EX_KEYBOARD lv_indev_drv_t kb_drv; lv_indev_drv_init(&kb_drv); kb_drv.type = LV_INDEV_TYPE_KEYPAD; /*键盘输入设备*/ kb_drv.read_cb = keyboard_read; /*从 PC 键盘读取按键*/ lv_indev_t* kb_indev = lv_indev_drv_register(&kb_drv); lv_indev_set_group(kb_indev, g); /*绑定到组 g*/ #endif #if LV_EX_MOUSEWHEEL lv_indev_drv_t enc_drv; lv_indev_drv_init(&enc_drv); enc_drv.type = LV_INDEV_TYPE_ENCODER; /*编码器输入设备*/ enc_drv.read_cb = mousewheel_read; /*从鼠标滚轮读取旋量*/ lv_indev_t* enc_indev = lv_indev_drv_register(&enc_drv); lv_indev_set_group(enc_indev, g); #endif

启用前提是LV_USE_GROUP在 lv_conf.h 中被置为 1(当前仓库已开启),否则lv_group_t相关 API 不会编译。

3.2 控件如何入组

Demo 采用"按当前标签页动态清空/重建组"的策略。标签页切换事件回调tv_event_cb会先把所有对象移出组:

lv_group_remove_all_objs(g); /*清空组*/ ... uint16_t tab = lv_tabview_get_tab_act(tv); /*取当前活动标签页*/ if (tab == 0) { size = sizeof(selector_objs); objs = (lv_obj_t**)&selector_objs; } else if (tab == 1) { size = sizeof(textinput_objs); objs = (lv_obj_t**)&textinput_objs; } lv_group_add_obj(g, tv); /*先加入 tabview 本身*/ for (i = 0; i < size / sizeof(lv_obj_t*); i++) /*再逐个加入该页控件*/ { if (objs[i] == NULL) continue; lv_group_add_obj(g, objs[i]); }

见 lv_demo_keypad_encoder.c。这样一来,每个标签页的焦点序列就是"tabview → 页内全部控件",切换页后焦点集合自动更新,避免了把所有控件塞进一个组导致的焦点顺序混乱。

3.3 焦点回调focus_cb:自动滚动页面

组设置焦点回调lv_group_set_focus_cb(g, focus_cb),当焦点移动到组内某对象时触发。Demo 的focus_cb根据当前标签页把对应控件滚动到可视区域(带动画):

static void focus_cb(lv_group_t* group) { lv_obj_t* obj = lv_group_get_focused(group); if (obj != tv) { uint16_t tab = lv_tabview_get_tab_act(tv); switch (tab) { case 0: lv_page_focus(t1, obj, LV_ANIM_ON); break; /*Selectors 页*/ case 1: lv_page_focus(t2, obj, LV_ANIM_ON); break; /*Text input 页*/ } } }

见 lv_demo_keypad_encoder.c。这在控件数量超过一屏时尤为重要——焦点落到屏幕外的控件时,页面会自动滚动跟随,保证用户始终能看到当前聚焦项。

四、核心机制二:导航模式与编辑模式(navigate/edit mode)

4.1 双模式的定义

编码器交互天然需要两种操作语义:

  • 导航模式(navigate):旋转编码器 = 在控件之间移动焦点(lv_group_focus_next/prev)。
  • 编辑模式(edit):旋转编码器 = 调整当前控件的值(向左/向右发送LV_KEY_LEFT/RIGHT),例如滑块的数值、滚轮的选项。

这两个模式由组的editing标志控制,对应 API 为 lv_group_set_editing 与 lv_group_get_editing。

4.2 模式切换与按键处理的内核实现

LVGL 核心在处理编码器输入设备时(lv_indev.c)实现了完整的状态机:

  1. 短按 Enter:对可编辑控件,如果当前不在编辑模式,则进入编辑模式(lv_group_set_editing(g, true),见 lv_indev.c);对不可编辑控件,直接发送LV_EVENT_CLICKED等点击事件。
  2. 长按 Enter:在编辑模式与导航模式之间来回切换(lv_group_set_editing(g, lv_group_get_editing(g) ? false : true),见 lv_indev.c)。
  3. 旋转编码器:编辑模式下将每个 tick 转为LV_KEY_LEFT/RIGHT发送给焦点控件;导航模式下执行lv_group_focus_next/prev移动焦点(见 lv_indev.c)。
  4. 键盘左/右键:在编码器处理逻辑中还会被转换为enc_diff--/enc_diff++,即用键盘方向键也能模拟编码器旋转(见 lv_indev.c)。

此外,LV_KEY_ENTER按下时会先查询控件的可编辑性(LV_SIGNAL_GET_EDITABLE),再决定是直接"按下"还是进入编辑模式,这保证了 Button、Switch 等控件与 Slider、Spinbox 等控件在按键语义上的正确区分。

4.3 Demo 对编辑模式的使用

Demo 在弹出欢迎消息框时,针对编码器场景直接置为编辑模式并冻结焦点导航:

lv_group_add_obj(g, mbox); lv_group_focus_obj(mbox); #if LV_EX_MOUSEWHEEL lv_group_set_editing(g, true); /*进入编辑模式*/ #endif lv_group_focus_freeze(g, true); /*冻结焦点移动,只能操作当前对象*/

见 lv_demo_keypad_encoder.c。lv_group_focus_freeze(g, true)对应内核函数 lv_group_focus_freeze,用于在模态弹窗期间锁死焦点,避免滚轮误切焦点。用户选择 "Ok" 或 "Cancel" 后,msgbox_event_cb删除消息框、恢复lv_layer_top()样式,并向 tabview 发送LV_EVENT_REFRESH重新建立焦点集合(见 lv_demo_keypad_encoder.c)。

五、核心机制三:聚焦文本框时自动弹出虚拟键盘

5.1 交互流程

"Text input" 标签页演示了无触摸环境下的文本输入:聚焦文本框后自动创建 LVGL 虚拟键盘控件(lv_keyboard_create),并与之联动。关键代码在ta_event_cb(lv_demo_keypad_encoder.c):

if (e == LV_EVENT_FOCUSED) { lv_textarea_set_cursor_hidden(ta, false); /*显示光标*/ if (lv_group_get_editing(g)) { /*处于编辑模式*/ if (textinput_objs.kb == NULL) { textinput_objs.kb = lv_keyboard_create(lv_scr_act(), NULL); /*创建虚拟键盘*/ lv_group_add_obj(g, textinput_objs.kb); /*键盘也加入组,可被焦点导航*/ lv_obj_set_event_cb(textinput_objs.kb, kb_event_cb); lv_obj_set_height(tv, LV_VER_RES - lv_obj_get_height(textinput_objs.kb)); /*压缩 tabview 高度*/ } lv_keyboard_set_textarea(textinput_objs.kb, ta); /*键盘与文本框绑定*/ lv_group_focus_obj(textinput_objs.kb); /*焦点跳到键盘*/ lv_group_set_editing(g, true); lv_page_focus(t2, lv_textarea_get_label(ta), LV_ANIM_ON); } }

这里体现了两个设计要点:

  • 虚拟键盘本身也是一个 LVGL 控件,加入组g后即可被编码器/键盘导航与操作;
  • 弹出键盘时把 tabview 高度压缩为LV_VER_RES - 键盘高度,避免键盘遮挡文本区域。

5.2 键盘的关闭

kb_event_cb在用户点击虚拟键盘的 Apply / Cancel 时销毁键盘并恢复 tabview 高度:

static void kb_event_cb(lv_obj_t* kb, lv_event_t e) { lv_keyboard_def_event_cb(kb, e); /*先走默认处理*/ if (e == LV_EVENT_APPLY || e == LV_EVENT_CANCEL) { lv_group_focus_obj(lv_keyboard_get_textarea(kb)); /*焦点还给文本框*/ lv_obj_del(kb); textinput_objs.kb = NULL; lv_obj_set_height(tv, LV_VER_RES); /*恢复高度*/ } }

见 lv_demo_keypad_encoder.c。

六、驱动层与映射细节:PC 键盘、鼠标滚轮如何变成 LVGL 输入设备

6.1 键盘驱动:SDL 按键 → LVGL 按键码

驱动 keyboard.c 由 SDL 事件线程调用keyboard_handler()记录按键与按下状态,LVGL 周期性调用keyboard_read()读取。关键映射在keycode_to_ascii()(keyboard.c):

SDL 按键LVGL 键码用途
SDLK_RIGHT/SDLK_KP_PLUSLV_KEY_RIGHT焦点右移 / 数值增大
SDLK_LEFT/SDLK_KP_MINUSLV_KEY_LEFT焦点左移 / 数值减小
SDLK_UPLV_KEY_UP焦点上移
SDLK_DOWNLV_KEY_DOWN焦点下移
SDLK_ESCAPELV_KEY_ESC取消/退出
SDLK_BACKSPACELV_KEY_BACKSPACE删除
SDLK_DELETELV_KEY_DEL删除
SDLK_KP_ENTER/\rLV_KEY_ENTER确认/按下

普通字符键则原样透传(default: return sdl_key;),从而可以直接在文本框中输入字母与数字。

6.2 滚轮驱动:鼠标滚轮 → 编码器

驱动 mousewheel.c 维护一个enc_diff累计旋量与按下状态,mousewheel_read()每次读取后将其清零(enc_diff = 0)。事件处理:

case SDL_MOUSEWHEEL: enc_diff = -event->wheel.y; /*向下滚动 = 正向旋转*/ break; case SDL_MOUSEBUTTONDOWN: if (event->button.button == SDL_BUTTON_MIDDLE) state = LV_INDEV_STATE_PR; /*中键=编码器按钮*/ break; case SDL_MOUSEBUTTONUP: if (event->button.button == SDL_BUTTON_MIDDLE) state = LV_INDEV_STATE_REL; break;

见 mousewheel.c。在嵌入式环境中,把这段逻辑替换为读取真实旋转编码器 GPIO(例如定时器计数增量)与按键状态即可,接口完全一致(enc_diff+state)。

6.3 与触摸输入的共存

模拟器hal_init()同时注册了鼠标指针输入设备(LV_INDEV_TYPE_POINTER)与键盘输入设备。指针设备不依赖 Group,而键盘/编码器输入设备依赖 Group;两者可共存于同一应用——触摸点击与按键导航并行工作,这是 LVGL 输入设备模型的多设备特性。

七、把 Demo 机制移植到 HoloCubic 固件的要点

HoloCubic 的嵌入式固件位于2.Firmware/HoloCubic-fw,其输入系统同样基于本仓库配套的 LVGL 库(见lib/lvgl),因此本 Demo 的交互模型可直接借鉴:

  1. 创建 Group 并绑定输入设备:固件端 lv_port_indev.c 注册触摸/按键输入设备后,为需要键盘导航的界面创建lv_group_create()并调用lv_indev_set_group()。
  2. 控件入组:把可导航控件逐个lv_group_add_obj();多页面场景参考 Demo 的"切页重建组"策略,避免焦点落在不可见控件上。
  3. 设置焦点回调:结合滚动容器使用lv_page_focus(),让焦点始终可见。
  4. 处理编辑模式:若硬件只有旋转编码器,依赖内核默认的"短按进入编辑、长按切换模式"即可;若要固定某一控件处于编辑状态,调用lv_group_set_editing(g, true)。
  5. 文本输入:需要输入时创建lv_keyboard并与 textarea 绑定,输入完成销毁,流程与ta_event_cb/kb_event_cb一致。

需要注意,本仓库模拟器使用的 LVGL 为v7.6.1时代接口(lv_drv_conf.h中标注 "Configuration file for v7.6.1"),函数签名如lv_obj_set_event_cb、lv_group_focus_freeze等与 v8/v9 存在差异;移植到新版本时需对照新版 API 调整。

八、小结

lv_demo_keypad_encoder以不到 340 行源码完整演示了 LVGL 无触摸交互的全部关键机制:

  • Group 对象组:定义焦点遍历序列,lv_group_add_obj/lv_group_focus_next/prev/lv_group_set_focus_cb构成导航骨架;
  • 导航/编辑双模式:编码器在移动焦点与调整数值间切换,内核在 lv_indev.c 中统一实现,短按/长按/旋转三种输入被映射为不同语义;
  • 动态焦点重建与模态冻结:标签页切换时重建组、弹窗期间lv_group_focus_freeze,保证焦点行为可控;
  • 虚拟键盘联动:文本框聚焦时弹出、Apply/Cancel 时销毁,兼顾编码器与键盘两种输入源。

对 HoloCubic 这类桌面显示站而言,若硬件方案包含旋转编码器或实体按键,这套机制即可作为界面导航与输入的现成范式;即便在 PC 上做 UI 调试,也可以像本仓库 lvgl_similator.cpp 一样,用鼠标滚轮 + 键盘精确复现真实硬件的操作手感。

  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

相关推荐

上一篇:Wand-Enhancer 免费解锁指南:零成本升级 WeMod 专业版体验的完整实操手册
下一篇:使用 SWIFT 一站式微调与推理通义千问 Qwen 系列大模型

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 11:29:59

2026年,薪酬设计咨询公司如何用科学分钱激活团队?

2026年的人才争夺战&#xff0c;早已不是简单加薪就能解决的。大量企业HR向我反馈&#xff0c;他们面临的困境高度一致&#xff1a;竞争对手的市场数据不够精准&#xff0c;样本量模糊&#xff0c;一线城市AI算法岗的真实薪酬被笼统的全行业数据掩盖&#xff1b;薪酬数据时效性…

作者头像 李华
网站建设 2026/10/21 21:01:30

SAP CTO谈预测AI:能解释业务,不等于能准确预测业务

企业希望AI预测客户付款、商品需求和设备故障&#xff0c;但这些问题并不适合全部交给语言模型。在CIO的采访中&#xff0c;SAP首席技术官Philipp Herzig指出&#xff0c;分类、回归等结构化数据任务&#xff0c;需要更合适的预测工具&#xff1b;SAP正在通过RPT和TabPFN等表格…

作者头像 李华
网站建设 2026/10/18 23:26:40

AnyPS5全攻略:存储扩展、远程串流与散热维护实战

我拿到“AnyPS5”这个标题时&#xff0c;第一反应是&#xff1a;这哥们儿多半不是来晒机器的&#xff0c;而是想告诉所有人——不管你是哪个版本、光驱还是数字版、新玩家还是老折腾党&#xff0c;手里的PS5都能玩出更多花样。后来跟群里的朋友一聊&#xff0c;发现大家关心的还…

作者头像 李华
网站建设 2026/10/19 5:19:07

STM32F103ZET6循迹小车实战:从硬件选型到PID调参全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华