3-3_使用物理按键代替触摸(groups)
-
组(Groups)——像电视遥控器的频道列表
- 想象每个组是一个电视频道列表,我们把电视按钮(界面元素)加入这个列表后,用遥控器(物理按键)就能在这些按钮间切换。
- 比如遥控器上下键选择不同节目,确认键打开选中的节目。
-
输入设备类型——不同类型的遥控器
- 触摸板像手机触屏,键盘像电脑键盘,编码器像老式收音机的旋钮(左转/右转/按下)
- 每个遥控器(输入设备)要指定控制哪个电视频道列表(组)
-
默认组——出厂预设频道
- 按钮、滑块等控件出厂时自带"默认遥控列表"
- 但需要我们先买个遥控器(创建组)并绑定这个默认列表才能使用
1. 自定义组:自己造一个遥控器
想象你要用遥控器控制电视里的菜单按钮,步骤如下:
-
① 造遥控器(创建组)
lv_group_t *g = lv_group_create(); // 相当于买了一个新遥控器
-
② 设置频道列表(添加对象到组)
lv_group_add_obj(g, btn1); // 把按钮1加入遥控器的控制列表 lv_group_add_obj(g, btn2); // 把按钮2也加入列表
就像把「换台」「音量」按钮加入遥控器的可操作列表。
-
③ 绑定遥控器(关联输入设备)
lv_indev_set_group(encoder, g); // 让旋钮(encoder)使用这个遥控器
相当于给遥控器装上电池,告诉它该控制哪台电视。
2. 默认组:系统自带的遥控器
LVGL 自带一个「默认遥控器」,但需要先激活才能用:
- ① 初始化默认组(相当于给遥控器通电)
lv_group_t *default_group = lv_group_create(); // 先造一个组 lv_group_set_default(default_group); // 标记为「系统默认遥控器」
- ② 自动关联
当你创建按钮、滑块等控件时,它们会自动加入默认组(省去手动添加的步骤):lv_obj_t *btn = lv_btn_create(lv_scr_act()); // 按钮自动加入默认遥控器列表
- ③ 绑定输入设备
lv_indev_set_group(keypad, default_group); // 让键盘控制默认组
3. 类比生活场景
- 自定义组:
你买了一个万能遥控器,手动设置它只能控制客厅的灯和空调(精准控制特定对象)。 - 默认组:
电视自带的遥控器,所有新买的机顶盒、音响都会自动匹配它(省心但需要提前设置)。
4. 注意事项
- 代码中的“红色关键词”:
lv_group_create()
:造遥控器的核心指令lv_group_add_obj()
:往遥控器列表里塞按钮lv_indev_set_group()
:绑定遥控器和操作设备(如旋钮、键盘)
- 常见错误:
如果没创建组就直接用,就像拿着没电池的遥控器狂按——系统不会有反应!
总结
- 自定义组适合需要精准控制的场景(比如游戏手柄只控制角色移动)。
- 默认组适合快速开发,系统帮你自动管理(比如简单菜单导航)。
- 核心操作就三步:造遥控器 → 塞按钮 → 绑设备,其他都是系统帮你完成的“魔法”。
-
关键按键——遥控器必备按键
- 方向键(←→↑↓):像游戏手柄方向键切换选项
- 回车键(Enter):相当于"确认"按钮
- 左右键(Next/Prev):频道切换键,快速跳转选项
-
最重要且建议必要的按键是:
LV_KEY_NEXT/PREV
LV_KEY_ENTER
LV_KEY_UP/DOWN/LEFT/RIGHT在我们回调函数 read_cb 中,应该优先考虑将现有的键对应转换为上面这些键,以便能在组中导航并与所选对象进行交互。
其实一般来说,只使用 LV_KEY_LEFT/RIGHT 就足够了,因为大多数对象都可以通过它们被完全控制。
对于编码器,默认只使用 LV_KEY_LEFT、LV_KEY_RIGHT 和 LV_KEY_ENTER
-
编码器的两种模式——空调遥控器的两种状态
- 导航模式:旋钮左右转切换温度/风速选项(切换焦点)
- 编辑模式:选中温度后,转动旋钮调整具体数值(修改数值)
- 短按旋钮切换模式,就像空调遥控器上的"模式"键
-
如果我们使用编码器,因为编码器的按键有限,所以在各个对象之间进行导航就不方便了。这个时候就需要区分模式了,lvgl帮我们区分了两种模式:编辑和导航模式
在导航模式下,编码器的 LV_KEY_LEFT/RIGHT 被转换为 LV_KEY_NEXT/PREV,这样就可以通过转动编码器来选择下一个或上一个对象。
如果需要修改对象的值,比如有个滑杆(lv_slider)代表音量或亮度,我们通过按下 LV_KEY_ENTER 将模式切换为编辑模式,这样我们可以通过转动编码器来修改滑杆的值。然后根据对象的类型,短按或长按 LV_KEY_ENTER 切换为导航模式(离开编辑模式)。
-
对象状态变化——手机APP的选中效果
- 聚焦状态:像选中微信图标时的蓝色高亮
- 编辑状态:进入聊天框输入时,光标闪烁效果
- 我们可以自定义这两种状态的显示样式
-
如果通过触摸板点击选中对象或通过编码器或键盘聚焦对象,对象的状态会变为 LV_STATE_FOCUSED。
如果对象进入编辑模式,对象将进入 LV_STATE_FOCUSED | LV_STATE_EDITED 状态。
所以,我们可以根据上面这两点,为对象设置被选中或聚焦或编辑时的样式。
-
实现步骤(比喻版)
- ① 买新遥控器(lv_group_create)
- ② 把电视按钮加入遥控列表(lv_group_add_obj)
- ③ 给遥控器装上电池(lv_indev_set_group)
- ④ 设置默认遥控器(lv_group_set_default)
-
假设你买了一台新电视(LVGL 界面):
- 找到自带遥控器(默认组):
lv_group_t *default_group = lv_group_create(); // 拆开遥控器包装
- 装电池(绑定输入设备):
lv_indev_set_group(keypad, default_group); // 给遥控器装上电池
- 标记为默认:
lv_group_set_default(default_group); // 告诉系统“这是主遥控器”
- 自动匹配控件:
之后创建的所有按钮、滑块会自动加入这个默认组,就像电视的换台、音量键自动匹配遥控器。
实际应用场景举例:
用旋钮控制智能家居面板:
- 左转旋钮:在"灯光""空调""窗帘"间切换(导航模式)
- 按下旋钮:进入选中的控制项
- 右转旋钮:调整灯光亮度/空调温度(编辑模式)
- 长按旋钮:返回主菜单
这种设计让硬件按键也能像触摸屏一样流畅操作界面,特别适合嵌入式设备的物理按键控制。
基础对象的作用
1. 基础对象:给所有控件预装“智能芯片”
基础对象就像给每个控件(按钮、滑块等)装了一个通用智能芯片。这个芯片内置了以下功能:
- 状态感知模块:能自动检测是否被聚焦(类似台灯感应到有人靠近)
- 事件响应电路:当被点击/触摸时,自动触发预设动作(如变色、弹出菜单)
- 外观变形器:根据状态切换样式(聚焦时边框发光,按下时颜色变深)
2. 为什么后续控件“天生会响应”?
2.1 预先埋入“感应线”
基础对象在创建时,已经内置了三条关键逻辑线:
-
状态线:
LV_STATE_FOCUSED
(聚焦状态)- 当物理按键导航或触摸选中时,自动激活该状态
- 例如:用方向键切换按钮时,系统自动给当前按钮挂上“聚焦”标签
-
样式线:
LV_STYLE_...
(视觉反馈规则)- 预设了聚焦时的边框加粗、背景色渐变等效果
- 类似智能台灯的“有人靠近就亮暖光,离开切冷光”的规则
-
事件线:
lv_event_cb
(动作反馈机制)- 当检测到点击/触摸事件时,自动调用注册的回调函数
- 例如按钮被按下时播放“咔哒”音效(通过回调函数实现)
2.2 继承机制:子控件“免配置”
当开发者创建按钮、滑块等子控件时:
- 自动继承父级基础对象的所有能力(如同新生儿继承父母的基因)
- 无需重复编程:不用再写聚焦检测、样式切换等底层代码
3. 生活场景类比
想象用遥控器操作智能电视菜单:
- 基础对象 = 电视系统内核
- 预装所有菜单项的导航规则(上下键切换、确认键选中)
- 子控件 = 具体菜单项
- 每个菜单项只需关注自己的功能(如“设置”打开设置页)
- 无需关心“如何被选中”“选中后怎么高亮”——系统内核已自动处理
4. 技术实现揭秘
通过三个关键设计实现自动化响应:
- 状态机机制
// 系统内部自动更新对象状态(伪代码) if (按键方向右按下) { 当前聚焦对象.state = LV_STATE_FOCUSED; // 去掉旧焦点 右侧对象.state = LV_STATE_FOCUSED; // 设置新焦点 } [3,5](@ref)
- 样式级联
// 基础对象预定义的聚焦样式 lv_style_set_border_color(&focus_style, LV_COLOR_RED); // 聚焦时红边框[5](@ref)
- 事件冒泡
// 触摸事件从子对象向父对象传递(如点击按钮触发屏幕刷新) void 基础对象_事件处理() { if (事件类型==点击) 执行注册的回调函数; // 自动调用子控件的事件函数[6,8](@ref) }
总结:基础对象就像“智能工厂流水线”
- 预制组件:提前安装好传感器(状态检测)、执行器(样式变化)、控制器(事件处理)
- 批量生产:后续控件直接从流水线下线,自带完整交互能力
- 开发者只需:告诉控件“做什么”(业务逻辑),而不用操心“怎么做”(交互响应)
这种设计让 LVGL 的开发效率大幅提升,就像用预制板盖房子——不用从烧砖开始,直接搭积木就能建成大厦。
3-4_开关(lv_switch)
一、开关是什么?
就像手机里的WiFi开关,轻轻一滑就能开启/关闭功能。LVGL的开关有3个部分:
- 背景:底层底板
- 指示器:显示状态的长条(比如关闭时灰色,开启时蓝色)
- 旋钮:可滑动的小圆点
-
开关包括以下3个零件:
LV_PART_MAIN 背景。 修改其 padding 会让下面的(指示器)在相应方向上的大小发生变化。
LV_PART_INDICATOR 显示开关状态的指示器。
LV_PART_KNOB 在指标左侧或右侧的旋钮。 默认情况下,旋钮是圆形的,边长等于滑块的较小边。 可以修改 padding 值使旋钮变大,填充值可以是不对称的。示例: // 修改开关背景部分的颜色 lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_MAIN); // 修改 pad,改变指示器在相应方向上的大小 lv_obj_set_style_pad_left(sw, 5, LV_PART_MAIN); // lv_obj_set_style_pad... // 修改开关状态指示器部分,关闭状态时的背景颜色 lv_obj_set_style_bg_opa(sw, 100, LV_PART_INDICATOR); lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_INDICATOR); // 修改开关状态指示器部分,打开状态时的背景颜色 lv_obj_set_style_bg_color(sw, lv_color_hex(0x7719aa), LV_PART_INDICATOR | LV_STATE_CHECKED); // 修改开关旋钮部分的颜色 lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_KNOB);
二、如何创建一个开关?
lv_obj_t *sw = lv_switch_create(父容器); // 类似把开关装进一个盒子里
三、如何给开关"化妆"?
可以分别给三个部分设置颜色、大小:
-
改背景颜色:
lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_MAIN); // 设置成番茄红
-
调整指示器大小:
lv_obj_set_style_pad_left(sw, 5, LV_PART_MAIN); // 左边缩进5像素,指示器变窄
-
改旋钮样式:
lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_KNOB); // 旋钮变红色
四、开关的两种状态
- 开启:触发
LV_STATE_CHECKED
(像灯泡亮起) - 关闭:无特殊状态
-
当开关被打开时,开关的状态会变为 LV_STATE_CHECKED 。我们可以通过下面这两个接口获取开关当前的状态:
lv_obj_has_state(switch, LV_STATE_CHECKED); // 返回 bool 类型, 开-1 ; 关-2 lv_obj_get_state(switch); // 返回枚举类型: LV_STATE_...
一般我们通过触摸或按键控制让开关 打开/关闭,其实我们还可以通过下面这个接口来主动 打开/关闭:
lv_obj_add_state(switch, LV_STATE_CHECKED); // 开 lv_obj_clear_state(switch, LV_STATE_CHECKED); // 关
我们可以通过下面的接口让按钮处于不可更改状态:
lv_obj_add_state(sw, LV_STATE_DISABLED); // 当前状态是关,并且不可更改 lv_obj_add_state(sw, LV_STATE_CHECKED | LV_STATE_DISABLED); // 当前状态是开,并且不可更改
要让按钮恢复可以更改的状态,我们只要将 LV_STATE_DISABLED 清除即可:
lv_obj_clear_state(switch, LV_STATE_ DISABLED); // 清除禁用状态,按钮可正常使用
检测状态:
if(lv_obj_has_state(sw, LV_STATE_CHECKED)) {
printf("开关已开启!");
}
手动控制:
lv_obj_add_state(sw, LV_STATE_CHECKED); // 强行打开
lv_obj_clear_state(sw, LV_STATE_CHECKED); // 强行关闭
五、让开关"锁住"
// 锁住且保持开启状态(像灰色不可点的按钮)
lv_obj_add_state(sw, LV_STATE_CHECKED | LV_STATE_DISABLED);
// 解锁
lv_obj_clear_state(sw, LV_STATE_DISABLED);
六、点击开关时触发事件
当用户滑动开关时,会触发一个特殊信号:
lv_obj_add_event_cb(sw, event_handler, LV_EVENT_VALUE_CHANGED, NULL);
// 事件处理函数示例
void event_handler(lv_event_t *e) {
if(lv_event_get_code(e) == LV_EVENT_VALUE_CHANGED) {
printf("状态改变了!");
}
}
事件代码的完整分类(部分常用)
事件类型 触发场景
LV_EVENT_PRESSED 对象被按下(类似手机重按)
LV_EVENT_RELEASED 对象被释放(松手)
LV_EVENT_LONG_PRESSED 长按对象超过设定时间
LV_EVENT_FOCUSED 对象获得焦点(比如被方向键选中)
LV_EVENT_DEFOCUSED 对象失去焦点
LV_EVENT_KEY 检测到按键输入时
事件对象 e
七、用键盘控制开关(适合遥控器)
- 方向键➡️/⬆️:开启
- 方向键⬅️/⬇️:关闭
- 确认键(ENTER):切换状态
比喻理解
把开关想象成手电筒:
- 背景:手电筒外壳
- 指示器:显示是否通电的灯罩
- 旋钮:推动开关的按钮
- 状态:推上去亮灯(CHECKED),按下来灭灯
这样是不是更清晰了呢?实际开发时就像在搭积木,组合这些功能就能做出漂亮的界面啦!
3-5_复选框(lv_checkbox)
一、复选框是啥?
就像你填问卷时的多选题小方框,点一下打勾✓,再点取消。在LVGL里它由两部分组成:左边的小方框(勾选框)和右边的文字标签。
二、怎么创建一个复选框?
代码很简单,就像生个孩子:
lv_obj_t * cb = lv_checkbox_create(父对象); // 父对象比如是窗口或屏幕
三、打扮复选框——改样式
复选框包括以下3个零件:
LV_PART_MAIN 背景。 修改其 pad_column 会影响勾选框和标签之间的间距。LV_PART_INDICATOR 勾选框,默认情况下是一个具有默认背景样式属性的正方形,它的大小等于主要部分字体的高度。 填充属性使复选框在相应方向上变大。
示例:
// 修改开关背景部分的颜色
lv_obj_set_style_bg_opa(cb, 100, LV_PART_MAIN);
lv_obj_set_style_bg_color(cb, lv_color_hex(0xc43e1c), LV_PART_MAIN);
// 修改勾选框和标签之间的间距
lv_obj_set_style_pad_column(cb, 100, 0);或 lv_obj_set_style_pad_column(cb, 100, LV_PART_MAIN);
// 修改勾选框部分,勾选时的背景颜色
lv_obj_set_style_bg_color(cb, lv_color_hex(0xc43e1c), LV_PART_INDICATOR);
// 修改勾选框部分,不勾选时的背景颜色lv_obj_set_style_bg_color(cb,lv_color_hex(0x7719aa), LV_PART_INDICATOR | LV_STATE_CHECKED);
-
改背景颜色和间距(比如让文字离勾选框远点):
// 背景颜色(整个复选框的底色) lv_obj_set_style_bg_color(cb, 颜色代码, LV_PART_MAIN); // 调整勾选框和文字的距离(数字越大离得越远) lv_obj_set_style_pad_column(cb, 20, LV_PART_MAIN);
-
改勾选框的样式:
// 未勾选时的颜色(比如紫色) lv_obj_set_style_bg_color(cb, lv_color_hex(0x7719aa), LV_PART_INDICATOR); // 勾选后的颜色(比如红色) lv_obj_set_style_bg_color(cb, lv_color_hex(0xc43e1c), LV_PART_INDICATOR | LV_STATE_CHECKED);
四、控制复选框的状态
-
判断是否勾选:
if(lv_obj_has_state(cb, LV_STATE_CHECKED)) { // 打钩了,做点事情 }
-
手动勾选/取消:
lv_obj_add_state(cb, LV_STATE_CHECKED); // 强制打钩 lv_obj_clear_state(cb, LV_STATE_CHECKED); // 取消打钩
-
禁用复选框(变灰不能点):
lv_obj_add_state(cb, LV_STATE_DISABLED); // 禁用且未勾选 // 或者禁用且保持勾选状态 lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
五、处理点击事件
当用户点击导致勾选状态变化时,触发事件:
lv_obj_add_event_cb(cb, 事件处理函数, LV_EVENT_VALUE_CHANGED, NULL);
// 事件处理函数示例
void 事件处理函数(lv_event_t * e) {
if(lv_obj_has_state(cb, LV_STATE_CHECKED)) {
printf("你勾选了!");
} else {
printf("取消勾选啦~");
}
}
六、用键盘/按键控制
如果你的设备有物理按键:
- 按上/右键:勾选
- 按下/左键:取消勾选
- 按回车键:切换勾选状态
七、避坑指南
- 修改样式时注意区分
LV_PART_MAIN
(整体背景)和LV_PART_INDICATOR
(小方框) - 禁用状态用的是
LV_STATE_DISABLED
,恢复用lv_obj_clear_state
- 文档里有个小笔误:最后恢复禁用状态时应该是
cb
而不是switch
(switch是另一个控件)
总结:复选框就是一个能打钩的小控件,你可以随意调整它的外观样式,通过代码控制它的状态,还能监听用户的勾选操作。就像给用户提供了一个可以开关的选项,特别适合做多选题或功能开关。
3-6_下拉列表(lv_dropdown)
下拉列表(lv_dropdown)是 LVGL 中一个常用的控件,类似我们手机里点选菜单的弹窗。下面用大白话解释它的核心知识点:
1. 下拉列表是什么?
- 就像点外卖时弹出的菜单,你点击后会弹出一堆选项,选一个后菜单消失,只显示你选的选项。
- 用途:让用户从多个选项中选一个(比如选择语言、设置模式)。
2. 创建下拉列表
lv_obj_t *dropdown = lv_dropdown_create(parent); // 在父对象上创建下拉列表
- 就像在屏幕上贴了一个按钮,点击后会弹出选项列表。
3. 设置选项
lv_dropdown_set_options(dropdown, "选项1\n选项2\n选项3"); // 用换行符分隔选项
- 选项用
\n
分隔,类似写一个多行字符串。 - 例如:设置成 "苹果\n香蕉\n橘子",用户点开就能看到三个水果选项。
4. 方向控制
lv_dropdown_set_dir(dropdown, LV_DIR_TOP); // 向上弹出(默认向下)
- 如果屏幕下方空间不够,可以让列表向上弹。
5. 显示图标
lv_dropdown_set_symbol(dropdown, LV_SYMBOL_SETTINGS); // 右侧显示设置图标
- 可以在下拉按钮旁边加个小图标(比如齿轮、箭头)。
6. 获取用户选择
uint16_t selected_id = lv_dropdown_get_selected(dropdown); // 获取选中项的序号(从0开始)
char buf[32];
lv_dropdown_get_selected_str(dropdown, buf, sizeof(buf)); // 获取选中项的文本
- 比如用户选了第二个选项(香蕉),
selected_id
就是 1,buf
里是 "香蕉"。
7. 事件响应
lv_obj_add_event_cb(dropdown, my_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
- 当用户选择了一个新选项时,会触发
LV_EVENT_VALUE_CHANGED
事件,你可以在回调函数里处理结果。
8. 样式自定义
- 可以修改下拉按钮的颜色、字体、边框等:
lv_obj_set_style_bg_color(dropdown, lv_color_hex(0x3498db), LV_PART_MAIN); // 按钮背景色
lv_obj_set_style_text_font(dropdown, &my_font, LV_PART_MAIN); // 修改字体
- 还能调整弹出列表的样式,比如选项高亮颜色。
9. 小技巧
- 默认选中:用
lv_dropdown_set_selected(dropdown, 1)
默认显示第二个选项。 - 禁止编辑:
lv_dropdown_set_text(dropdown, "固定提示文字")
可以禁止用户选择(只显示提示)。
比喻理解:
- 下拉列表就像一盒饼干,平时只显示最上面的一块(当前选项),点击后盒子展开,能看到所有饼干(选项列表),拿一块后盒子又合上。
通过以上知识点,你可以在 LVGL 中实现一个灵活的下拉菜单,满足用户交互需求。实际开发时结合官方文档的示例代码会更直观哦!
在 LVGL 中,事件回调的实现涉及对象的事件分发机制和状态管理。以下是简化后的内部原理代码逻辑(非真实源码,但能说明核心思想):
一、事件注册阶段:lv_obj_add_event_cb
// 伪代码:将回调函数存入对象的“事件链表”中
void lv_obj_add_event_cb(lv_obj_t *obj, lv_event_cb_t cb, lv_event_code_t filter, void *user_data) {
// 创建事件回调节点
lv_event_dsc_t *dsc = malloc(sizeof(lv_event_dsc_t));
dsc->cb = cb; // 保存用户的事件处理函数
dsc->filter = filter; // 事件类型(如 LV_EVENT_VALUE_CHANGED)
dsc->user_data = user_data;
// 将节点挂到对象的事件链表中
dsc->next = obj->event_list;
obj->event_list = dsc;
}
- 核心逻辑:每个对象维护一个“事件回调链表”,用户通过
lv_obj_add_event_cb
将回调函数注册到链表中。
二、事件触发阶段:值改变时发送事件
// 伪代码:当对象的值改变时(如勾选状态变化)
void lv_dropdown_set_checked(lv_obj_t *obj, bool checked) {
// 更新对象状态(如 LV_STATE_CHECKED)
if (checked) obj->state |= LV_STATE_CHECKED;
else obj->state &= ~LV_STATE_CHECKED;
// 发送事件 LV_EVENT_VALUE_CHANGED
lv_event_t e;
e.target = obj; // 事件的目标对象
e.type = LV_EVENT_VALUE_CHANGED; // 事件类型
e.user_data = NULL; // 用户数据
// 遍历事件链表,调用匹配的回调函数
lv_event_dsc_t *dsc = obj->event_list;
while (dsc) {
if (dsc->filter == LV_EVENT_VALUE_CHANGED) {
dsc->cb(&e); // 执行用户的事件处理函数
}
dsc = dsc->next;
}
}
一、代码作用:控制对象的「勾选状态」
就像开关灯一样,这段代码用来设置或取消对象(比如复选框、按钮)的「已勾选」状态。例如:
勾选复选框 ✅ → 设置 LV_STATE_CHECKED
取消勾选 → 移除 LV_STATE_CHECKED
二、代码逐句解析
if (checked)
obj->state |= LV_STATE_CHECKED; // 打开勾选状态(类似开灯)
else
obj->state &= ~LV_STATE_CHECKED; // 关闭勾选状态(类似关灯)
1. obj->state |= LV_STATE_CHECKED
作用:在现有状态基础上添加勾选状态。
类比:你本来有「正常状态」(比如默认颜色),现在加上「勾选状态」(比如变成红色),两者可以共存。
二进制原理:用按位或操作(|)设置特定比特位。假设 LV_STATE_CHECKED 是二进制 0000 0010,操作后该位会被置为 1。
2. obj->state &= ~LV_STATE_CHECKED
作用:在现有状态中移除勾选状态。
类比:把已经勾选的「小红点」擦掉,但保留其他状态(比如禁用、聚焦)。
二进制原理:用按位与操作(&)和取反操作(~)清除特定比特位。例如 LV_STATE_CHECKED 取反后是 1111 1101,与原状态按位与后该位会被清零。
三、为什么要用位操作?
高效:直接操作二进制位,速度极快(适合嵌入式设备)。
灵活:允许对象同时存在多个状态(比如同时勾选 + 聚焦 + 禁用)。
不干扰其他状态:只修改勾选相关的比特位,其他状态(如 LV_STATE_FOCUSED)不受影响。
四、实际应用场景举例
复选框交互
用户点击复选框时,通过这段代码切换勾选状态,界面自动刷新显示对勾 ✔️ 或空框 □。
按钮选中效果
单选按钮组中,选中一个按钮后设置 LV_STATE_CHECKED,其他按钮取消该状态。
状态联动
勾选后可能触发其他操作(如禁用输入框):
if (lv_obj_has_state(obj, LV_STATE_CHECKED)) {
lv_obj_add_state(input, LV_STATE_DISABLED); // 勾选后禁用输入框
}
五、扩展知识
其他常见状态:
LV_STATE_DISABLED(禁用)、LV_STATE_FOCUSED(聚焦)、LV_STATE_PRESSED(按下)等,均通过类似位操作管理。
状态检测:
通过 lv_obj_has_state(obj, LV_STATE_CHECKED) 判断是否勾选。
- 核心逻辑:当对象状态变化时(如勾选/取消),内部调用
lv_event_send
,遍历事件链表并触发匹配的回调。
三、状态检测阶段:lv_obj_has_state
// 伪代码:检查对象是否处于特定状态(如 LV_STATE_CHECKED)
bool lv_obj_has_state(lv_obj_t *obj, lv_state_t state) {
return (obj->state & state) == state; // 按位与判断状态位
}
- 核心逻辑:对象的状态由二进制位表示(如
LV_STATE_CHECKED
是某个位),通过位运算快速判断。
四、完整流程示例(用户代码)
// 用户注册事件回调
lv_obj_add_event_cb(dropdown, event_handler, LV_EVENT_VALUE_CHANGED, NULL);
// 当用户点击下拉列表时:
// 1. 内部更新对象状态(如设置 LV_STATE_CHECKED)
// 2. 发送 LV_EVENT_VALUE_CHANGED 事件
// 3. 遍历事件链表,找到用户注册的 event_handler 并调用
// 事件处理函数
void event_handler(lv_event_t *e) {
lv_obj_t *obj = e->target;
if (lv_obj_has_state(obj, LV_STATE_CHECKED)) {
printf("Checked!");
} else {
printf("Unchecked!");
}
}
五、关键设计思想
- 事件驱动模型:LVGL 通过事件链表实现松耦合交互,用户只需关注注册回调。
- 状态位管理:对象状态用二进制位存储,高效检测(如
LV_STATE_CHECKED
对应一个位)。 - 事件冒泡(高级特性):事件可向父对象传递,源码中会有更复杂的链表遍历逻辑。
通过这种机制,LVGL 可以高效处理用户交互事件,同时保持代码的灵活性和可扩展性。
3-7_滚轮(lv_roller)
一、滚轮是啥?
滚轮就是一个可以上下滑动选择选项的控件,类似你手机设置时间时那个转动的列表。比如选年月日、选模式、选菜单项时都很好用。
二、它能干啥?
-
显示多个选项
你可以给它一堆文字选项(比如"苹果、香蕉、橘子"),它会自动排列成一个列表。 -
滑动选择
用户上下滑动(或拖动)就能选择不同的选项,手感流畅。 -
自动居中
选中的选项会自动“吸”到中间位置(默认行为,可改)。 -
事件反馈
比如选完后可以触发一个事件(比如更新屏幕上的其他内容)。
三、怎么配置它?
-
设置选项
用字符串数组表示选项,用\n
分隔,比如:lv_roller_set_options(roller, "苹果\n香蕉\n橘子", LV_ROLLER_MODE_NORMAL);
-
外观调整
- 文字大小:调大调小字体。
- 颜色:改文字颜色、背景色。
- 动画时间:滑动时的动画快慢(比如设置 300ms 更顺滑)。
-
方向和对齐
- 可以改成水平滚动(默认是垂直)。
- 选项左对齐、居中还是右对齐。
-
循环滚动
开启后,滑到最后一个选项会跳回第一个,无限循环(比如选月份时)。
四、怎么知道用户选了啥?
-
获取选中项
用lv_roller_get_selected(roller)
拿到选项的索引(比如0是第一个,1是第二个)。 -
绑定事件
当用户选完时,可以触发回调函数(比如更新一个标签显示当前选中的内容):lv_obj_add_event_cb(roller, my_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
五、举个栗子🌰
假设做一个选择水果的功能:
// 1. 创建滚轮
lv_obj_t *roller = lv_roller_create(lv_scr_act());
// 2. 设置选项(3个水果)
lv_roller_set_options(roller, "苹果\n香蕉\n橘子", LV_ROLLER_MODE_NORMAL);
// 3. 设置位置和大小
lv_obj_set_size(roller, 100, 200);
lv_obj_center(roller);
// 4. 绑定事件:当选项变化时打印选中的水果
lv_obj_add_event_cb(roller, event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// 事件回调函数
void event_cb(lv_event_t *e) {
lv_obj_t *roller = lv_event_get_target(e);
uint16_t selected = lv_roller_get_selected(roller);
printf("你选了:%s\n", lv_roller_get_options(roller) + selected * 4); // 假设每个选项占4字节
}
六、实际应用场景
- 智能家居中选模式(如“制冷、制热、通风”)。
- 智能手表中设置闹钟时间。
- 任何需要用户从多个选项中选择一个的场景。
七、总结
- 滚轮 = 滑动选择列表,配置简单,交互直观。
- 记住关键函数:设选项、改样式、拿选中值、绑事件。
- 动手试一次比看十遍更管用!参考文档里的代码改一改,立马就会了。
学习顺利!如果卡住了,就去文档里搜关键词,或者到百问网论坛提问~ 😊
LVGL 的 lv_bar
控件用于显示进度或数值范围,通俗理解就像一个“进度条”。以下是其核心知识点和用法,用简单易懂的方式讲解:
3-8_进度条(lv_bar)
1. 基础用法
1.1 创建进度条
就像在屏幕上“画”一个空白的进度条:
lv_obj_t *bar = lv_bar_create(lv_scr_act()); // 在屏幕上创建进度条
1.2 设置范围
进度条需要一个“最小值”和“最大值”。比如,0~100 表示从 0% 到 100%:
lv_bar_set_range(bar, 0, 100); // 范围设为 0~100
1.3 设置当前值
让进度条显示当前进度。例如,显示 70%:
lv_bar_set_value(bar, 70, LV_ANIM_ON); // 设置值为70,带动画效果
LV_ANIM_ON
表示进度条会“平滑过渡”到目标值,像慢慢填充的动画。
2. 两种模式
2.1 普通模式(默认)
从左到右填充,比如常见的下载进度条:
lv_bar_set_mode(bar, LV_BAR_MODE_NORMAL); // 默认模式,可不设置
2.2 对称模式
从中间向两侧延伸,适合显示有正负的范围。例如,温度计(中间是0,左边-50,右边+50):
lv_bar_set_range(bar, -50, 50); // 范围设为-50~50
lv_bar_set_mode(bar, LV_BAR_MODE_SYMMETRICAL); // 对称模式
lv_bar_set_value(bar, 30, LV_ANIM_OFF); // 右侧填充到30
3. 自定义样式
3.1 背景和填充颜色
- 背景(灰色)对应进度条的“空白部分”。
- 填充色(蓝色)对应当前进度。
// 设置背景样式
lv_style_set_bg_color(&style_bg, lv_color_hex(0xCCCCCC)); // 灰色背景
lv_style_set_radius(&style_bg, 5); // 圆角5像素
// 设置填充部分样式
lv_style_set_bg_color(&style_indicator, lv_palette_main(LV_PALETTE_BLUE)); // 蓝色填充
lv_style_set_radius(&style_indicator, 5); // 圆角5像素
// 应用样式到进度条
lv_obj_add_style(bar, &style_bg, LV_PART_MAIN); // 背景样式
lv_obj_add_style(bar, &style_indicator, LV_PART_INDICATOR); // 填充样式
3.2 调整大小
进度条默认是横向的,宽高可自由调整:
lv_obj_set_size(bar, 200, 20); // 宽度200像素,高度20像素(横向)
4. 高级技巧
4.1 动画时间
控制进度条填充动画的速度:
lv_bar_set_anim_time(bar, 1000); // 动画持续1秒(1000毫秒)
4.2 垂直进度条
将进度条改为垂直方向,只需让高度 > 宽度,并调整填充方向:
lv_obj_set_size(bar, 20, 200); // 宽20,高200(垂直方向)
lv_obj_set_style_base_dir(bar, LV_BASE_DIR_RTL, 0); // 从下到上填充(部分版本需测试)
5. 实际场景举例
- 下载进度条:范围 0~100,普通模式,蓝色填充。
- 音量平衡:范围 -100~100,对称模式,中间为静音,左右为音量。
- 电池电量:根据值动态更新颜色(如红色低电量,绿色高电量)。
总结
lv_bar
的核心操作就是:设置范围 → 设置值 → 调整样式。结合动画和模式,可以灵活实现各种进度效果。就像搭积木一样,先搭框架,再涂颜色!
3-9_滑动条(lv_slider)
一、滑动条是啥?
想象你手机上的音量条,手指一拖,声音变大变小,这就是滑动条!LVGL的滑动条 就是让你在屏幕上做一个类似的拖拽控件,用来调节数值(比如亮度、音量、进度等)。
二、滑动条怎么用?三步搞定!
-
创建滑动条(就像在屏幕上放个空白条):
lv_obj_t *slider = lv_slider_create(lv_scr_act()); // 创建一个滑动条,放在当前屏幕
-
设置基本属性:
- 方向:横着还是竖着?
lv_obj_set_width(slider, 200); // 水平滑动条:宽度大一点 lv_obj_set_height(slider, 20); // 高度小一点 // 垂直滑动条反过来设置高度大、宽度小
- 取值范围:默认0~100,比如改成0~255(调亮度):
lv_slider_set_range(slider, 0, 255); // 最小0,最大255
- 当前值:比如默认音量50:
lv_slider_set_value(slider, 50, LV_ANIM_OFF); // 不要动画,直接设成50
- 方向:横着还是竖着?
-
加点样式(让它变漂亮):
- 轨道颜色(滑动的背景条):
lv_obj_set_style_bg_color(slider, lv_color_hex(0x808080), LV_PART_MAIN); // 灰色轨道
- 进度条颜色(已滑过的部分):
lv_obj_set_style_bg_color(slider, lv_color_hex(0x00FF00), LV_PART_INDICATOR); // 绿色进度
- 滑块样式(那个可拖动的圆点):
lv_obj_set_style_bg_color(slider, lv_color_hex(0xFF0000), LV_PART_KNOB); // 红色滑块 lv_obj_set_style_radius(slider, LV_RADIUS_CIRCLE, LV_PART_KNOB); // 变成圆形
- 轨道颜色(滑动的背景条):
三、滑动条动了怎么办?——事件处理
当用户拖动滑块时,你需要知道当前值。比如拖动时实时显示数值:
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
// 回调函数
void slider_event_cb(lv_event_t *e) {
lv_obj_t *slider = lv_event_get_target(e);
int value = lv_slider_get_value(slider); // 获取当前值
printf("当前值:%d\n", value);
}
四、小技巧和常见坑
- 显示数值:滑动条本身不显示数字,你可以在旁边放个标签(
lv_label
),在事件回调中更新它。 - 动画效果:设置值时加上动画更丝滑:
lv_slider_set_value(slider, 80, LV_ANIM_ON); // 滑块会慢慢滑到80
- 垂直滑动条:设置高度大于宽度,自动变垂直!
- 取值范围不对:记得先设范围再设值,否则可能无效。
五、总结比喻
- 创建滑动条 → 买了个空白音量条
- 设置范围 → 贴个标签写“最低静音,最高震耳”
- 设置样式 → 把灰色轨道涂绿,滑块涂红
- 事件回调 → 耳朵贴着喇叭,一拖动就听到声音变化
这样是不是超清楚?试着代码跑一跑,调个颜色或数值,立刻有成就感!
3-10_圆弧(lv_arc)
1. 什么是圆弧(Arc)?
- 简单理解:圆弧是一个可以显示弧形进度或角度的部件,比如钟表的表盘、音量控制旋钮、加载进度条等。
- 在LVGL中:
lv_arc
是LVGL提供的一个基础部件,可以通过代码控制它的起始角度、结束角度、颜色、样式等。
2. 关键参数
- 角度范围:
- 默认角度范围是
0°
(类似钟表的3点方向)到360°
(一圈)。 - 课程中提到的
0°/360°
、90°
(12点方向)、180°
(9点方向)、270°
(6点方向)是常见角度参考点。
- 默认角度范围是
- 起始角度和结束角度:
- 通过设置起始角度(
start_angle
)和结束角度(end_angle
),可以控制弧形的显示范围。 - 例如:设置
start_angle=90
,end_angle=270
,会显示从12点方向到6点方向的半圆形。
- 通过设置起始角度(
3. 基础功能
- 显示模式:
- 可以显示为静态的弧形,或动态的进度条(比如加载动画)。
- 交互性:
- 支持用户拖动圆弧改变值(类似旋钮调节)。
- 样式自定义:
- 修改弧宽(粗细)、颜色、背景色、圆角等属性。
4. 代码示例(伪代码)
// 创建圆弧
lv_obj_t* arc = lv_arc_create(parent);
lv_arc_set_angles(arc, 90, 270); // 设置起始和结束角度
lv_arc_set_bg_angles(arc, 0, 360); // 设置背景弧范围
lv_arc_set_value(arc, 60); // 设置当前值(进度)
lv_arc_set_rotation(arc, 180); // 旋转整个圆弧
5. 常见应用场景
- 进度指示器:显示下载进度、加载进度。
- 旋钮控制:模拟音量旋钮、亮度调节。
- 仪表盘:显示速度、温度等范围值。
6. 工具推荐
- SquareLine Studio:课程中提到的
https://squareline.io
是LVGL官方的可视化设计工具,可以通过拖拽快速设计圆弧等部件,生成代码。 - 在线文档:
- 中文文档:适合快速查阅参数和示例(http://lvgl.100ask.net)。
- 英文文档:内容更新更及时(https://docs.lvgl.io)。
7. 学习建议
- 先通过文档中的示例代码修改参数,观察圆弧变化。
- 结合事件回调(如
LV_EVENT_VALUE_CHANGED
)实现交互功能。 - 用SquareLine Studio设计界面,再导出代码学习实现原理。
通过这个部件,你可以轻松实现各种弧形UI效果!如果需要具体代码案例或某个功能的详细说明,可以再问我哦! 😊
3-11_定时器(lv_timer)
📝 LVGL定时器(lv_timer)通俗教程
1️⃣ 定时器是什么?
- 类比:就像你手机里的闹钟,设定一个时间后自动响铃。
- 在LVGL中:定时器用来让程序“过一会儿”自动做某件事(比如更新界面、检测按键)。
2️⃣ 创建定时器(三步搞定)
// 第一步:写个回调函数(闹钟响铃时要做的事)
void my_task(lv_timer_t *timer) {
printf("时间到!执行任务!\n");
}
// 第二步:创建定时器(设置闹钟)
lv_timer_t *timer = lv_timer_create(my_task, 1000, NULL);
// 1000ms后触发,NULL表示不传参数
3️⃣ 关键功能详解
- 周期设置:
lv_timer_set_period(timer, 2000)
→ 改为2秒触发一次。 - 传递参数:通过
user_data
传参(比如传递按钮对象):lv_obj_t *btn = lv_btn_create(lv_scr_act()); lv_timer_create(my_task, 1000, btn); // 传按钮给回调函数
- 单次执行:回调函数中调用
lv_timer_del(timer)
,就像一次性的闹钟。
4️⃣ 删除定时器
lv_timer_del(timer); // 关掉闹钟,不再执行
5️⃣ 注意事项
- 别做太重任务:回调函数里不要卡太久(比如复杂计算),否则界面会卡。
- 自动销毁:删除对象时,关联的定时器不会自动删除,需手动删除避免内存泄漏。
- 精准度:定时器依赖LVGL主循环,如果主循环卡了,定时器也会延迟。
6️⃣ 典型应用场景
- 按钮颜色每隔1秒闪烁一次
- 进度条每100ms更新一次进度
- 检测长按事件(超过2秒触发)
🌰 完整示例:按钮倒计时
void countdown(lv_timer_t *timer) {
int *count = (int*)timer->user_data;
printf("倒计时: %d\n", (*count)--);
if(*count <= 0) lv_timer_del(timer);
}
void create_timer() {
int count = 5;
lv_timer_t *timer = lv_timer_create(countdown, 1000, &count);
}
// 输出:5 → 4 → 3 → 2 → 1 → 停止
💡 总结:LVGL定时器=智能闹钟,学会创建→设参数→删定时器
三招就能玩转界面动态效果!遇到问题记得查文档哦~
3-12_线条(lv_line)
LVGL中的lv_line
控件就像一支智能画笔,可以轻松画出各种线条和形状。以下是通俗易懂的解释:
功能简介
- 画线小能手:它能根据你设定的多个坐标点,自动连接成直线或折线(比如画三角形、多边形)。
- 灵活调整:线条颜色、粗细、端点形状都能自定义,还支持动态变化(如让线条动起来)。
使用三步曲
-
创建画布:
lv_obj_t *line = lv_line_create(parent); // 在父容器上创建线条控件
-
设置坐标点:
定义一个点数组(每个点包含x/y坐标),例如画一个"V"字形:static lv_point_t points[] = {{0, 0}, {50, 100}, {100, 0}}; // 三个点坐标 lv_line_set_points(line, points, 3); // 绑定到线条控件
-
美化样式:
lv_style_set_line_color(&style, lv_palette_main(LV_PALETTE_BLUE)); // 蓝色线条 lv_style_set_line_width(&style, 5); // 5像素粗 lv_style_set_line_rounded(&style, true); // 圆润端点 lv_obj_add_style(line, &style, 0); // 应用样式
动态效果示例
让线条的第二个点上下跳动:
// 在动画中更新点的y坐标
lv_anim_t anim;
lv_anim_init(&anim);
lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)anim_y_cb);
lv_anim_set_values(&anim, 80, 120); // y坐标在80~120之间变化
lv_anim_set_repeat_count(&anim, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&anim);
void anim_y_cb(lv_anim_t *anim) {
points[1].y = (int16_t)anim->current_value; // 动态修改第二个点y值
lv_line_set_points(line, points, 3); // 刷新线条
}
一、代码功能说明
这段代码实现了一个线条的第二个点上下跳动的动画效果。具体来说,线条的第二个点的Y坐标会在80到120像素之间循环移动,形成类似弹簧或波浪的动态效果。
二、代码逐行解析
1. 动画初始化部分
lv_anim_t anim; // 创建一个动画对象(就像买了个空白录像带)
lv_anim_init(&anim); // 初始化动画参数(给录像带贴标签)
- 作用:创建一个动画的“容器”,准备设置具体参数。
2. 设置动画关键参数
// 告诉动画:“每次变化时要调用anim_y_cb函数去更新坐标”(录像带里录什么内容)
lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)anim_y_cb);
// 设置Y坐标的变化范围:从80到120(从低到高来回跳)
lv_anim_set_values(&anim, 80, 120);
// 设置动画无限循环(像单曲循环播放歌曲)
lv_anim_set_repeat_count(&anim, LV_ANIM_REPEAT_INFINITE);
- 关键点:
anim_y_cb
是动画的“执行函数”,每次动画更新时都会调用它- 80和120是Y坐标的起始和结束位置
3. 启动动画
lv_anim_start(&anim); // 按下播放键,开始执行动画
4. 动画执行函数(核心逻辑)
void anim_y_cb(lv_anim_t *anim) {
// 获取当前动画的Y值(比如当前是90、100、110...)
int16_t current_y = (int16_t)anim->current_value;
// 修改线条第二个点的Y坐标(动态改变点的位置)
points[1].y = current_y;
// 刷新线条显示(告诉屏幕:“快重新画线!”)
lv_line_set_points(line, points, 3);
}
- 形象比喻:就像有只无形的手在上下拖动线条的第二个点,每拖动一次就刷新画面
三、代码运行流程
- 初始化动画 → 买录像带、贴标签
- 设置动画参数 → 录制备忘录(怎么动、动多久)
- 启动动画 → 按下播放键
- 每次动画更新 → 调用
anim_y_cb
函数:- 获取当前Y值 → 看看手拉到哪了
- 修改点坐标 → 移动点的位置
- 刷新线条 → 让屏幕显示新位置
四、可调整的参数(自由发挥)
参数 | 作用 | 示例值 |
---|---|---|
lv_anim_set_values | 控制Y坐标范围 | (0, 200)更大幅度跳动 |
添加动画时间lv_anim_set_time | 控制跳动速度(默认无时间限制) | 500毫秒更快 |
添加路径函数lv_anim_set_path_cb | 改变运动曲线(如弹跳效果) | lv_anim_path_bounce 1 6 |
五、举个实际例子
想象你正在做心电图波形动画:
- 定义一个心电图线条的多个点
- 用类似代码动态修改某个关键点的Y坐标
- 就会看到心电图波形有规律地起伏!
常见用途
- 绘制图表折线
- 创建自定义图形边框
- 实现进度条、波浪动画等动态效果
小贴士
- 点坐标默认为相对父容器的位置,也可通过
lv_obj_set_align()
调整基准点。 - 通过
lv_style_set_line_dash_width(&style, 10)
可画出虚线效果。
总结:lv_line
把复杂的绘图简化为设置点坐标+调整样式,像连点游戏一样简单有趣!
LVGL 的文本框(lv_textarea)可以理解为手机上的"输入框",用来实现用户输入文字、密码或显示多行文本。以下是通俗易懂的讲解:
3-13_文本框(lv_textarea)
📝 基础概念
- 文字容器:像一个透明盒子,可以显示文字、支持键盘输入
- 功能多样:
- 支持单行/多行模式(如聊天输入框)
- 密码模式(显示***)
- 添加提示文字(如"请输入手机号")
- 限制输入内容(如只允许数字)
🔧 常用操作(比喻为手机操作)
-
创建文本框:
lv_textarea_create(parent)
- 相当于在屏幕上新建一个空白输入框
-
设置尺寸位置:
lv_obj_set_size(textarea, 200, 60); // 设置大小 lv_obj_center(textarea); // 居中显示
-
添加文字:
- 插入文字:
lv_textarea_add_char(textarea, 'A')
- 追加文字:
lv_textarea_add_text(textarea, "Hello")
- 插入文字:
-
密码模式:
lv_textarea_set_password_mode(textarea, true); // 开启密码***
-
提示文字:
lv_textarea_set_placeholder_text(textarea, "点击输入...");
🎛️ 进阶功能
-
输入限制:
lv_textarea_set_accepted_chars(textarea, "0123456789"); // 只允许输入数字
-
单行/多行切换:
lv_textarea_set_one_line(textarea, true); // 强制单行(类似手机验证码输入框)
-
事件处理:
// 当内容变化时触发回调 lv_obj_add_event_cb(textarea, text_changed_event, LV_EVENT_VALUE_CHANGED, NULL);
📌 使用步骤(以创建密码输入框为例)
- 创建基础文本框
- 设置尺寸和位置
- 开启密码模式
- 添加提示文字
- 限制输入为数字
- 添加内容变化监听
⚠️ 注意事项
- 需要配合键盘部件才能输入
- 多行模式下要手动处理滚动条
- 中文输入需要额外配置字库
- 长文本建议开启
LV_LABEL_TEXT_SELECTION
💡 举个栗子
// 创建一个带提示的密码输入框
lv_obj_t * pwd_ta = lv_textarea_create(lv_scr_act());
lv_textarea_set_password_mode(pwd_ta, true);
lv_textarea_set_placeholder_text(pwd_ta, "请输入6位密码");
lv_textarea_set_max_length(pwd_ta, 6);
lv_textarea_set_accepted_chars(pwd_ta, "0123456789");
lv_obj_align(pwd_ta, LV_ALIGN_TOP_MID, 0, 20);
通过这个结构可以快速实现类似微信密码输入框的效果,建议配合官方文档中的示例代码实践练习。
3-14_按钮矩阵(lv_btnmatrix)
一、按钮矩阵是什么?
像乐高积木一样排列的按钮组
可以一次性创建多个整齐排列的按钮(比如计算器键盘、菜单选项)。相比单独创建多个按钮,它更节省代码、布局更方便。
二、按钮矩阵的创建步骤
-
定义按钮文字
用一个字符串数组表示每个按钮的文字,例如:static const char *btn_map[] = {"1", "2", "3", "\n", "4", "5", "6", ""};
\n
表示换行,最后以空字符串""
结尾。 -
创建按钮矩阵对象
lv_obj_t *btnm = lv_btnmatrix_create(lv_scr_act()); lv_btnmatrix_set_map(btnm, btn_map); //绑定按钮文字
-
调整大小和位置
可以手动设置大小,或根据内容自动调整:lv_obj_set_size(btnm, 200, 150); // 或 lv_btnmatrix_set_btn_width(btnm, 0, 2); //第0列按钮占2个单位宽度
三、常用设置技巧
-
按钮布局控制
lv_btnmatrix_set_btn_ctrl_all(btnm, LV_BTNMATRIX_CTRL_CHECKABLE);
让所有按钮可以被选中(类似开关效果)lv_btnmatrix_set_one_checked(btnm, true);
只允许一个按钮被选中(类似单选按钮)
-
样式修改
修改按钮颜色、圆角等(示例):lv_obj_set_style_bg_color(btnm, lv_color_hex(0xE0E0E0), LV_PART_MAIN); lv_obj_set_style_bg_color(btnm, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
-
事件处理
监听按钮点击事件:lv_obj_add_event_cb(btnm, btnm_event_handler, LV_EVENT_VALUE_CHANGED, NULL);
在回调函数中获取被点击的按钮:
void btnm_event_handler(lv_event_t *e) { lv_obj_t *btnm = lv_event_get_target(e); uint16_t btn_id = lv_btnmatrix_get_selected_btn(btnm); printf("你点击了第%d个按钮\n", btn_id); }
四、实际应用场景
- 计算器界面:用3行4列的矩阵排列数字和运算符
- 菜单导航:纵向排列多个选项按钮
- 快捷工具栏:横向排列常用功能图标
五、避坑指南
- 数组结尾必须加空字符串
""
,否则会内存溢出! - 按钮索引 从0开始(第一行第一个是0,换行后继续递增)
- 中文显示需要先加载中文字库
通过这种方式,你可以快速创建整齐划一的按钮组,代码量减少80%!试试做一个简易计算器吧~ ✨
6-4_通过FreeType显示字体(lv_freeType)
1. FreeType 是什么?
- 比喻:想象你有一堆不同包装的糖果(比如盒子装、袋装、罐装),每种包装打开方式都不同。FreeType 就像一个万能开箱器,不管什么包装的字体文件(比如.ttf、.otf等格式),它都能帮你快速打开,并且把里面的“糖果”(字体)漂亮地摆出来(显示在屏幕上)。
- 作用:让LVGL(一个显示界面的工具)能显示更多样式的字体,尤其是放大后依然清晰(矢量字体),不像像素字体放大后会模糊。
2. 为什么需要它?
- 场景:比如你想在智能手表上显示“你好,世界!”这句话,用普通字体可能只能固定大小,但用FreeType可以随意调整字体大小,还能用各种酷炫字体(比如手写体、艺术字)。
- 优势:免费、支持格式多、高效(适合内存小的设备,比如单片机)。
3. Windows 配置步骤(重点)
-
第一步:拿“开箱器”
去Github或Gitee下载编译好的FreeType库(就像直接买现成的开箱器,不用自己造)。 -
第二步:放对位置
把下载的库文件解压后,复制到C:\freetype
(就像把工具放进工具箱,方便以后找)。 -
第三步:系统“认路”
把freetype.dll
复制到C:\Windows
(就像把开箱器说明书贴在家里的墙上,所有程序都能看到它)。 -
结果:现在LVGL就能用FreeType显示各种字体了!
4. CodeBlocks 配置(给开发工具“指路”)
- 比喻:你告诉CodeBlocks(开发工具):“我的开箱器在C盘freetype文件夹里,头文件在这,库文件在那”。
- 操作:在CodeBlocks里设置头文件路径和库文件路径(就像给导航设置目的地)。
5. ESP32/Linux 后续(拓展学习)
- 提示:课程后续会教如何在嵌入式设备(比如ESP32开发板)或Linux系统上使用FreeType,适合做智能家居、车载屏幕等项目。
6. 文档和资源
- 查字典:遇到问题可以去百问网的中文文档(像中文说明书),或者LVGL官网(像英文原版说明书)。
- 社区求助:论坛和淘宝店铺相当于“技术交流群”和“售后服务”。
总结
- FreeType = 万能字体显示工具,让LVGL能玩转各种字体。
- 配置 = 下载工具 → 放对位置 → 系统认路 → 开发工具指路。
- 应用:适合做需要灵活字体显示的项目,比如电子书、智能设备界面。
这样是不是更清晰啦?如果要做个小项目,比如用LVGL显示一首诗,用FreeType就能轻松换字体和大小啦! 😄
x-1_添加新的图标字体SYMBOL
-
什么是图标字体?
就像我们电脑里的宋体、楷体一样,但字体中的每个"字符"其实是各种小图标(比如房子、箭头、WiFi符号)。这样做的好处是图标可以像文字一样随意缩放、变色,比图片更节省资源。 -
为什么用FontAwesome?
- 这是全球最流行的免费图标库(有2000+精美图标)
- 比如微信图标、下载箭头、音乐符号等应有尽有
- 类似手机APP里的那些常用小图标都能找到
-
添加步骤类比:
就像给手机安装新字体:- ① 下载字体文件(就像.ttf文件)
- ② 把需要的图标"字符"导入LVGL
- ③ 使用时就像打字一样输入特定符号就能显示图标
-
必须注意的版权问题:
- 免费版(FontAwesome Free)可用于商业项目,但必须注明来源
- 如果修改了图标,必须改名不能叫FontAwesome
- 详细规则见课程提供的license链接
-
实际开发流程:
- 官网选图标 → 获取对应Unicode编码 → 生成字体文件 → 集成到LVGL
- 例如:选中"❤"图标,它的Unicode是
U+2764
,代码中写LV_SYMBOL_HEART
就能显示
-
为什么需要文档链接?
- 中文文档:方便英语不好的开发者
- 英文文档:获取最新版本的功能说明
- 官方论坛:遇到问题时可以查询类似案例
-
学习建议:
- 先尝试添加现成的FontAwesome图标
- 熟练后再学习制作自己的图标字体
- 遇到问题优先查阅提供的文档链接
举个例子: 想做一个音乐播放器界面,可以直接使用FontAwesome的播放按钮图标(▶),暂停图标(⏸),音量图标(🔈),无需自己画图,直接通过字体调用即可。
后续学习中如果遇到具体操作问题,可以重点参考文档中的代码示例和配置说明。记住图标字体是嵌入式开发中优化UI资源占用的重要手段哦!