【奶茶Beta专项】【LVGL9.4源码分析】09-core-group
2026/5/23 11:59:07 网站建设 项目流程

【奶茶Beta专项】【LVGL9.4源码分析】09-core-group焦点组管理

  • 1 概述
    • 1.1 文档目的
    • 1.2 代码版本与范围
  • 2 设计意图与总体定位
    • 2.1 为什么需要 `lv_group`
    • 2.2 `lv_group` 在架构中的位置
    • 2.3 与全局输入/焦点状态的关系
  • 3 使用方式与典型 DEMO
    • 3.1 创建 group 并绑定编码器输入
    • 3.2 在菜单/表单中使用 group
  • 4 接口分类与 API 速查表
    • 4.1 group 创建与销毁
    • 4.2 对象与 group 的关联管理
    • 4.3 焦点对象管理与导航
    • 4.4 group 行为与模式配置
    • 4.5 输入设备与 group 的绑定
    • 4.6 默认 group 与“自动加入 group”的组件
  • 5 设计优势与缺点(含案例)
    • 5.1 设计优势
    • 5.2 潜在缺点与注意事项
    • 5.3 简单案例:设置菜单与数值调节
    • 5.4 多 group 场景下的焦点切换参考方案
  • 6 与其它框架的对比与改进思路
    • 6.1 与 AWTK 的对比
    • 6.2 与 Qt 的对比
    • 6.3 与 Android / HTML/CSS 的对比
    • 6.4 可能的改进方向
  • 7 小结
  • 8 附录
    • A 参考文档(外部)
    • B 相关资源(CSDN 系列)

文档版本: 1.0
更新日期: 2025年12月
适用对象: 需要在 LVGL9.4 中实现键盘/编码器等焦点导航的工程师(嵌入式 GUI 框架维护者与应用开发者)

1 概述

1.1 文档目的

本篇聚焦library/lvgl/src/core/lv_group*,从“焦点与输入路由”角度解析 LVGL9.4 中 group 机制的设计思路与在系统中的定位,说明它如何把若干对象组合成一个“可通过键盘/编码器顺序导航”的焦点组,以及这对桌面类 UI、遥控器/旋钮交互的实际意义。
通过典型用法和常见问题,本篇希望帮助读者建立一套清晰的 group 使用心智:什么时候需要创建 group、如何为输入设备绑定 group、如何在工程中设计合理的焦点路径,并为后续性能与结构优化提供参考。

1.2 代码版本与范围

  • 仓库路径:https://github.com/lvgl/lvgl.git
  • 版本:v9.4.0
  • 主要关注源码文件:
    • library/lvgl/src/core/lv_group.h
    • library/lvgl/src/core/lv_group.c
  • 关联模块与调用者:
    • 输入设备(特别是编码器/键盘)与lv_indev_t
    • 对象系统lv_obj_t及其焦点状态;
    • 主题和样式中与“焦点态”相关的展示效果。

2 设计意图与总体定位

2.1 为什么需要lv_group

对于只使用触摸或鼠标的 UI 来说,焦点管理可以非常简单:

  • 用户直接点到哪个对象,就把事件派发给它;
  • 焦点/高亮状态更多是视觉上的,而不是严格的“输入路由”概念。

但在以下场景中,这种方式就远远不够:

  • 使用编码器/方向键/遥控器导航 UI;
  • 使用硬件按键在多个控件间顺序切换;
  • 想要实现“表单式焦点跳转”、“菜单上下切换”等桌面式体验。

为此,LVGL 提供了lv_group机制,将一组对象纳入统一管理:

  • 把一批对象组织成一个“焦点组”(group);
  • 为某个输入设备(encoder/keyboard)绑定一个 group;
  • 当输入设备上产生“左/右/上下/确认”等事件时,由 group 决定当前焦点对象是谁、下一个是谁,并更新对象的焦点状态与高亮样式。

2.2lv_group在架构中的位置

从层次上看,lv_group主要位于:

  • 下接:输入设备(encoder/keyboard 等)与事件系统;
  • 上连:对象系统中的“可聚焦对象”(比如按钮、文本框、列表项等);
  • 旁连:主题/样式系统,用于在对象获得焦点时展示不同外观。

可以用一个简单的示意图表示:

输入设备 (encoder / keyboard) │ ▼ lv_indev_t │ (绑定 group) ▼ lv_group ┌───────────────┐ │ obj_1 obj_2 │ ← 一组可聚焦对象(按钮、输入框、列表项等) │ obj_3 obj_4 │ └───────────────┘ │ ▼ 对象系统 + 样式 (focus 高亮 / 焦点环等)

因此可以理解为:

lv_group是 LVGL 中承载“基于方向键/编码器的焦点导航逻辑”的中间层,把输入事件转化为“在一组对象之间移动焦点”。

2.3 与全局输入/焦点状态的关系

lv_group并不直接管理所有输入设备,只对绑定到它的输入设备负责:

  • 每个lv_indev_t可以选择性地绑定一个lv_group_t
  • 一个 group 可以被一个或多个输入设备使用(视应用需求而定);
  • 对象的LV_STATE_FOCUSED状态由 group 切换,主题可以基于该状态绘制焦点高亮。

这让 LVGL 能够支持多种配置:

  • 单 group + 单 encoder:典型菜单导航;
  • 多 group + 多 encoder:多区域独立导航;
  • group + 触摸混合:触摸直接选中对象,同时 encoder 在 group 内顺序切换。

3 使用方式与典型 DEMO

3.1 创建 group 并绑定编码器输入

一个典型的焦点导航场景代码结构大致如下:

/* 1. 创建 group */lv_group_t*g=lv_group_create();/* 2. 创建若干可聚焦对象 */lv_obj_t*btn1=lv_button_create(lv_screen_active());lv_obj_t*btn2=lv_button_create(lv_screen_active());lv_obj_t*ta=lv_textarea_create(lv_screen_active());/* 3. 把对象加入 group */lv_group_add_obj(g,btn1);lv_group_add_obj(g,btn2);lv_group_add_obj(g,ta);/* 4. 为编码器输入设备绑定 group */lv_indev_t*enc=lv_indev_create();/* 伪代码,根据实际驱动注册方式获取 */lv_indev_set_type(enc,LV_INDEV_TYPE_ENCODER);lv_indev_set_group(enc,g);

此后,旋转编码器、按下确认键等操作会由 LVGL 转换成“在 group 中移动焦点/激活当前对象”的行为:

  • 左/右/上/下:在 group 内切换当前焦点对象;
  • 确认:触发当前对象的点击/激活事件(例如按钮被按下、文本框获得输入焦点)。

3.2 在菜单/表单中使用 group

菜单/表单是 group 的高频应用场景:

  • 把一列按钮或表单项(文本输入、开关、滑条等)按顺序加入同一个 group;
  • 焦点顺序通常与视觉顺序一致(从上到下、从左到右);
  • 用户通过方向键/编码器快速在这些元素中移动焦点,并使用确认键进行操作。

示意代码:

lv_group_t*menu_group=lv_group_create();lv_obj_t*item_wifi=lv_button_create(scr);lv_obj_t*item_bluetooth=lv_button_create(scr);lv_obj_t*item_about=lv_button_create(scr);lv_group_add_obj(menu_group,item_wifi);lv_group_add_obj(menu_group,item_bluetooth);lv_group_add_obj(menu_group,item_about);lv_indev_set_group(enc_indev,menu_group);

配合主题中对LV_STATE_FOCUSED的样式定义,可以实现类似 TV/机顶盒菜单那样清晰的焦点高亮效果。

4 接口分类与 API 速查表

说明:具体函数/类型名以当前版本lv_group.h为准,这里按功能维度做整理。

4.1 group 创建与销毁

功能接口说明
创建 grouplv_group_create(void)创建一个新的焦点组
删除 grouplv_group_delete(group)删除整个 group,自动把其中的对象移出

4.2 对象与 group 的关联管理

功能接口说明
把对象加入 grouplv_group_add_obj(group, obj)把一个对象加入指定 group
从 group 移除对象lv_group_remove_obj(obj)把对象从其所在的 group 中删除
获取对象所属 grouplv_group_get_group(obj)查询对象当前属于哪个 group

4.3 焦点对象管理与导航

功能接口说明
获取当前焦点对象lv_group_get_focused(group)返回 group 中当前具有焦点的对象
设置当前焦点对象lv_group_focus_obj(obj)让某个对象成为所在 group 的焦点对象
焦点移动到下一项lv_group_focus_next(group)在 group 内按顺序移动到下一个对象
焦点移动到上一项lv_group_focus_prev(group)在 group 内按顺序移动到上一个对象

4.4 group 行为与模式配置

功能接口说明
设置 wrap 模式lv_group_set_wrap(group, true/false)焦点是否在首尾间循环
设置 focus 回调lv_group_set_focus_cb(group, cb)当焦点变化时触发的回调
设置编辑模式lv_group_set_editing(group, true/false)控制 group 是否处于“编辑模式”
查询编辑模式lv_group_get_editing(group)当前是否是编辑模式

4.5 输入设备与 group 的绑定

功能接口说明
为输入设备绑定 grouplv_indev_set_group(indev, group)让输入设备的焦点导航作用于指定 group
获取输入设备当前 grouplv_indev_get_group(indev)查询某输入设备当前绑定的 group

4.6 默认 group 与“自动加入 group”的组件

lv_group提供了“默认 group”相关的 API,用于简化键盘/编码器场景下的导航配置:

  • 通过lv_group_set_default(group)设置一个默认 group;
  • 通过lv_group_get_default()在需要时获取当前默认 group 指针。

结合lv_obj_class_init_obj()的实现,可以看到 LVGL9.4 实际上已经接好了“根据类配置自动加入默认 group”的逻辑:

  • lv_obj_class_init_obj()末尾,框架会执行:
    • lv_group_t * def_group = lv_group_get_default();
    • 如果存在默认 group,且lv_obj_is_group_def(obj)返回 true,则调用lv_group_add_obj(def_group, obj)
  • lv_obj_is_group_def(obj)会沿着对象类的继承链查找lv_obj_class_t::group_def字段:
    • 当某个类的group_def设置为LV_OBJ_CLASS_GROUP_DEF_TRUE时,该类实例在构造阶段就会自动加入默认 group;
    • 若为LV_OBJ_CLASS_GROUP_DEF_FALSE,则不会自动加入;
    • 缺省值是LV_OBJ_CLASS_GROUP_DEF_INHERIT,表示沿用基类的配置。

也就是说,默认 group 机制的真实控制开关是类描述中的group_def,而不是早期注释里提到的add_to_def_group
当前版本的官方 widgets 是否自动进默认 group,完全取决于各自类描述里对group_def的设置。实际工程中,如果希望自定义控件在创建时自动进入默认 group,应当在自定义lv_obj_class_t里显式把group_def设为LV_OBJ_CLASS_GROUP_DEF_TRUE
对于其余控件,则仍然可以通过显式调用lv_group_add_obj()精细控制哪些对象参与键盘/编码器导航。

5 设计优势与缺点(含案例)

5.1 设计优势

  • 把“焦点导航”从业务代码中抽象出来
    • 应用只需把相关对象加入 group,具体“上一个/下一个焦点是谁”由 group 统一负责;
    • 减少在业务层到处自己维护索引/指针链表的重复代码。
  • 支持多种输入设备与混合交互
    • 同一个 UI 可以同时支持触摸与编码器:触摸直接点选,编码器按 group 顺序导航;
    • 对于遥控器、物理按键等场景尤其友好。
  • 焦点状态与样式解耦
    • group 只负责逻辑焦点,实际高亮效果由样式/主题根据LV_STATE_FOCUSED决定;
    • 可以为不同主题提供不同风格的焦点框、阴影、缩放等效果。

5.2 潜在缺点与注意事项

  • 需要额外维护 group 结构
    • 一旦对象树发生动态变化(增删节点),需要记得同步更新 group,否则可能出现“看得到的控件无法通过键盘导航到”;
    • 当对象被删除时,要确保从 group 中也被正确移除。
  • 多 group 场景下的焦点切换策略需要自行设计
    • 比如左侧菜单一个 group,右侧内容区另一个 group,什么时候把焦点从一个 group 切到另一个,通常由业务逻辑决定;
    • 若设计不当,用户可能会感觉“焦点乱跳”或“卡在某个区域出不来”。
  • 与复杂布局/自定义控件结合时需要额外心智负担
    • 某些复合控件内部可能已经使用 group 或自管理焦点,和外层 group 叠加时要特别小心,避免冲突。

5.3 简单案例:设置菜单与数值调节

以常见的“设置菜单 + 数值调节界面”为例:

  • 左侧是一个菜单列表(Wi-Fi、蓝牙、声音等),右侧是对应设置项;
  • 编码器旋转用于在左侧菜单 item 之间切换,按下确认键进入右侧调节;
  • 在右侧调节界面中,编码器旋转用于调节滑条/数值,按下返回键切回左侧菜单。

可以用两个 group 来划分职责:

  • group_menu:管理左侧菜单 item 的焦点;
  • group_detail:管理右侧调节控件的焦点;
  • 在进入/退出详情界面时,根据状态切换 encoding 输入设备绑定的 group。

这种分层方式能最大限度减少焦点混乱,也让逻辑更易于维护。

5.4 多 group 场景下的焦点切换参考方案

结合上面的案例,可以抽象出一套通用的“多 group 焦点切换”设计方案,供工程实践参考:

  • 状态机建模

    • 定义 UI 处于哪个区域(如FOCUS_ON_MENU/FOCUS_ON_DETAIL/FOCUS_ON_DIALOG等枚举);
    • 焦点切换不直接在 group 间跳,而是先切换状态,再根据状态绑定对应 group。
  • 统一入口函数

    • 实现一个小工具函数ui_focus_set_state(new_state)
      • 解除当前输入设备与旧 group 的绑定;
      • 根据new_state绑定到新的 group(例如group_menugroup_detail);
      • 可选:在新 group 内调用lv_group_focus_obj()把焦点设置到合适的默认对象。
  • 清晰的触发规则

    • 例如:
      • 在菜单上按“确认”键 →FOCUS_ON_MENUFOCUS_ON_DETAIL
      • 在详情中按“返回”键 →FOCUS_ON_DETAILFOCUS_ON_MENU
      • 如果弹出对话框,则暂存当前状态,临时切到FOCUS_ON_DIALOG并绑定对话框 group。

这种“状态机 + 统一切换入口”的设计,让多 group 场景下的焦点行为更可预测,也更便于后期维护和扩展。

6 与其它框架的对比与改进思路

6.1 与 AWTK 的对比

  • AWTK 中同样存在“焦点链表/控件树上的 focus 管理”,并支持键盘/遥控器导航;
  • 相比之下:
    • LVGL 使用lv_group明确把一批对象组织成导航组,强调“显式分组”;
    • AWTK 更多是依赖控件树上的顺序与 Tab 索引来控制焦点移动。
  • 启发:
    • 在 LVGL 上,可以借鉴 AWTK 对“自动焦点顺序”的处理,为 group 增加更灵活的“自动聚焦顺序”策略,而不仅仅是“按加入顺序”。

6.2 与 Qt 的对比

  • Qt 的焦点管理依赖QWidget树和QFocusPolicy,通过 Tab 顺序/箭头键决定焦点移动;
  • Qt 中,焦点大多围绕“窗口级别”组织,而不是额外的 group 类型。
  • 对比来看:
    • lv_group类似“独立的焦点管理器”,可以跨越对象树的部分结构自定义焦点路径;
    • 这在嵌入式 UI 中对于“多个独立区域 + 遥控器导航”的场景更加灵活。

6.3 与 Android / HTML/CSS 的对比

  • Android:
    • 通过focusablenextFocusUp/Down/Left/Right等属性配置焦点路径;
    • 系统根据这些属性和 View 树做自动焦点导航。
  • HTML/CSS:
    • 通过tabindex控制 Tab 键顺序;
    • 浏览器内置默认焦点走向,但复杂页面往往需要手工调整。
  • 对 LVGL 的启发:
    • 在现有lv_group基础上,可以考虑为对象增加更丰富的“焦点导航提示”(类似nextFocus*tabindex),group 再根据这些提示决定下一个焦点对象;
    • 这样既保留 group 的显式分组优势,又能在复杂布局中更精细地控制焦点路径。

6.4 可能的改进方向

  • 从性能角度
    • 当 group 包含大量对象时,频繁在内部线性遍历切换焦点可能带来一定开销;
    • 可以考虑为 group 提供“跳表/索引缓存”等轻量优化手段,减少在复杂界面中切换焦点的遍历成本。
  • 从代码结构角度
    • 将与 group 强相关的逻辑(比如编辑模式、wrap 模式等)进一步解耦成小型策略对象,使得不同输入设备/不同 UI 模式可以选择不同策略;
    • 这样可以在不修改核心 group 代码的情况下,为特定项目注入定制的焦点策略。
  • 从 API 设计角度
    • 提供辅助工具函数,帮助开发者在调试时可视化 group 中对象的焦点顺序;
    • 增强与 UI 编辑器/配置工具的联动,让 group 配置可以在设计期完成,而不是全部写在 C 代码中。

7 小结

lv_group是 LVGL9.4 中承载“键盘/编码器焦点导航”的关键组件:

  • 它把一批对象组织成逻辑上的“焦点组”,并与输入设备绑定,用来决定焦点在各对象之间的流转路径;
  • 通过与对象状态和主题样式配合,lv_group能在资源受限的嵌入式设备上提供接近桌面/TV 级的焦点导航体验;
  • 在工程实践中,合理规划 group 粒度、焦点顺序和多 group 切换策略,是做好键盘/编码器交互体验的关键。

对有更复杂交互需求的项目,可以在lv_group之上构建更高层的“页面/区域导航管理器”,借鉴 AWTK、Qt、Android、Web 的成熟经验,进一步提升可维护性和可配置性。

8 附录

A 参考文档(外部)

  • LVGL 官方文档:输入设备与焦点
  • LVGL 官方文档:对象与状态
  • LVGL GitHub 仓库

B 相关资源(CSDN 系列)

  • 【奶茶Beta专项】【LVGL9.4源码分析】01-目录结构
  • 【奶茶Beta专项】【LVGL9.4源码分析】02-编译框架-Cmake详解
  • 【奶茶Beta专项】【LVGL9.4源码分析】03-显示框架-display
  • 【奶茶Beta专项】【LVGL9.4源码分析】04-OS抽象层
  • 【奶茶Beta专项】【LVGL9.4源码分析】05-标准库

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询