View 体系与视图树¶
概述¶
EmbeddedGUI 的 UI 构建基于视图树(View Tree)模型。所有可见元素都是 egui_view_t 的派生类型,通过 egui_view_group_t 容器组织成树形结构。这一设计借鉴了 Android 的 View 体系,在 C 语言中通过结构体组合和函数指针实现继承与多态。
egui_view_t 基类¶
egui_view_t 是所有视图的基类,定义在 src/widget/egui_view.h。每个视图实例包含以下核心属性:
struct egui_view
{
uint16_t id; // 视图唯一标识(调试用)
uint8_t is_enable : 1; // 是否启用
uint8_t is_visible : 1; // 是否可见
uint8_t is_gone : 1; // 是否隐藏(不占布局空间)
uint8_t is_pressed : 1; // 是否按下
uint8_t is_clickable : 1; // 是否可点击
uint8_t is_request_layout : 1; // 是否需要重新布局
egui_alpha_t alpha; // 透明度 (0-255)
egui_view_padding_t padding; // 内边距
egui_view_margin_t margin; // 外边距
egui_region_t region; // 相对父视图的位置和大小
egui_region_t region_screen; // 屏幕绝对坐标(自动计算)
egui_background_t *background; // 背景对象
egui_view_group_t *parent; // 父视图指针
const egui_view_api_t *api; // 虚函数表
};
可见性控制¶
视图有两种隐藏方式:
属性 |
效果 |
布局影响 |
|---|---|---|
|
不绘制,但仍占据布局空间 |
无 |
|
不绘制,不占据布局空间 |
触发父容器重新布局 |
虚函数表 (API Table)¶
每个视图类型通过 egui_view_api_t 提供多态行为:
struct egui_view_api
{
int (*dispatch_touch_event)(egui_view_t *self, egui_motion_event_t *event);
int (*on_touch_event)(egui_view_t *self, egui_motion_event_t *event);
int (*on_intercept_touch_event)(egui_view_t *self, egui_motion_event_t *event);
void (*compute_scroll)(egui_view_t *self);
void (*calculate_layout)(egui_view_t *self);
void (*request_layout)(egui_view_t *self);
void (*draw)(egui_view_t *self);
void (*on_attach_to_window)(egui_view_t *self);
void (*on_draw)(egui_view_t *self);
void (*on_detach_from_window)(egui_view_t *self);
};
子类通过定义自己的 API 表来覆盖父类行为,实现多态。
egui_view_group_t 容器¶
egui_view_group_t 继承自 egui_view_t,是所有容器类视图的基类。它通过双向链表管理子视图:
struct egui_view_group
{
egui_view_t base; // 基类
uint8_t is_disallow_intercept; // 禁止拦截触摸事件
uint8_t is_disallow_process_touch_event;// 禁止处理触摸事件
egui_view_t *first_touch_target; // 触摸事件目标
egui_dlist_t childs; // 子视图双向链表
};
子视图管理 API¶
API |
说明 |
|---|---|
|
添加子视图 |
|
移除子视图 |
|
清空所有子视图 |
|
获取子视图数量 |
|
获取第一个子视图 |
启用 EGUI_CONFIG_FUNCTION_SUPPORT_LAYER 后,还支持 Z 轴排序:
API |
说明 |
|---|---|
|
将子视图移到最前 |
|
将子视图移到最后 |
|
设置图层值 (0-255) |
视图树结构¶
系统启动后,egui_core 维护一棵全局视图树:
root_view_group (egui_core 内部)
+-- user_root_view_group
+-- Activity root_view (由 Activity 管理)
+-- group_1 (egui_view_group_t)
| +-- label_1 (egui_view_label_t)
| +-- button_1 (egui_view_button_t)
+-- group_2 (egui_view_linearlayout_t)
+-- image_1 (egui_view_image_t)
+-- switch_1 (egui_view_switch_t)
root_view_group:框架内部根节点,包含系统级视图(如调试信息)user_root_view_group:用户视图根节点,Activity 的视图挂载于此叶子节点:label、button、image 等不包含子视图的控件
视图生命周期¶
一个视图从创建到销毁经历以下阶段:
init --> add_child (attach) --> calculate_layout --> draw --> remove_child (detach)
| | |
| request_layout on_draw
| compute_scroll
v
on_attach_to_window on_detach_from_window
init:初始化视图结构体,设置默认值,绑定 API 表on_attach_to_window:视图被添加到视图树时调用calculate_layout:计算屏幕绝对坐标region_screendraw:绘制流程 – 检查可见性 -> 设置 alpha -> 绘制背景 -> 调用on_drawon_detach_from_window:视图从视图树移除时调用
draw 流程详解¶
egui_view_draw() 的执行步骤:
void egui_view_draw(egui_view_t *self)
{
// 1. 保存当前 canvas alpha
// 2. 检查 is_visible 和 is_gone
// 3. 清除 canvas mask
// 4. 混合视图自身 alpha 到 canvas
// 5. 绘制阴影(如果有)
// 6. 计算工作区域(与 PFB 的交集)
// 7. 绘制背景
// 8. 调用 on_draw(子类实现具体绘制)
// 9. 恢复 canvas alpha
}
常用 API 速查表¶
位置与大小¶
API |
说明 |
|---|---|
|
设置相对父视图的位置 |
|
设置视图宽高 |
|
滚动到指定位置 |
|
相对滚动 |
|
直接设置 region |
外观属性¶
API |
说明 |
|---|---|
|
设置透明度 (0-255) |
|
设置背景 |
|
设置可见性 |
|
设置是否隐藏 |
|
设置阴影效果 |
边距¶
API |
说明 |
|---|---|
|
设置内边距 |
|
四边统一内边距 |
|
设置外边距 |
|
四边统一外边距 |
交互¶
API |
说明 |
|---|---|
|
设置点击回调 |
|
复制并覆写 |
|
设置是否可点击 |
|
设置是否启用 |
|
标记需要重绘 |
代码示例¶
创建一个包含 label 和 button 的 group:
#include "egui.h"
// 声明视图
static egui_view_group_t my_group;
static egui_view_label_t my_label;
static egui_view_button_t my_button;
// 点击回调
static void on_button_click(egui_view_t *self)
{
egui_view_label_set_text(EGUI_VIEW_OF(&my_label), "Clicked!");
}
void my_page_init(egui_core_t *core)
{
// 初始化 group
egui_view_group_init(EGUI_VIEW_OF(&my_group), core);
egui_view_set_position(EGUI_VIEW_OF(&my_group), 10, 10);
egui_view_set_size(EGUI_VIEW_OF(&my_group), 200, 100);
// 初始化 label
egui_view_label_init(EGUI_VIEW_OF(&my_label), core);
egui_view_set_position(EGUI_VIEW_OF(&my_label), 0, 0);
egui_view_set_size(EGUI_VIEW_OF(&my_label), 200, 30);
egui_view_label_set_text(EGUI_VIEW_OF(&my_label), "Hello EGUI");
// 初始化 button
egui_view_button_init(EGUI_VIEW_OF(&my_button), core);
egui_view_set_position(EGUI_VIEW_OF(&my_button), 0, 40);
egui_view_set_size(EGUI_VIEW_OF(&my_button), 100, 40);
egui_view_set_on_click_listener(EGUI_VIEW_OF(&my_button), on_button_click);
// 构建视图树
egui_view_group_add_child(EGUI_VIEW_OF(&my_group), EGUI_VIEW_OF(&my_label));
egui_view_group_add_child(EGUI_VIEW_OF(&my_group), EGUI_VIEW_OF(&my_button));
// 添加到用户根视图
egui_core_add_user_root_view(core, EGUI_VIEW_OF(&my_group));
}
继承关系图¶
egui_view_t (基类)
+-- egui_view_label_t
+-- egui_view_image_t
+-- egui_view_group_t (容器基类)
+-- egui_view_linearlayout_t
+-- egui_view_gridlayout_t
+-- egui_view_scroll_t
+-- egui_view_viewpage_t
+-- egui_view_button_t
+-- egui_view_switch_t
+-- ...
所有容器类控件(button、switch 等)都继承自 egui_view_group_t,因为它们内部可能包含子视图(如 button 内部的 label)。纯显示类控件(label、image)直接继承 egui_view_t。