LVGL学习(三)(groups,开关,复选框,下拉列表,滚轮,进度条,滑动条,圆弧,定时器,线条,文本框,按钮矩阵,通过FreeType显示字体,添加新的图标字体SYMBOL)

3-3_使用物理按键代替触摸(groups)

  1. 组(Groups)——像电视遥控器的频道列表​

    • 想象每个组是一个电视频道列表,我们把电视按钮(界面元素)加入这个列表后,用遥控器(物理按键)就能在这些按钮间切换。
    • 比如遥控器上下键选择不同节目,确认键打开选中的节目。
  2. ​输入设备类型——不同类型的遥控器​

    • 触摸板像手机触屏,键盘像电脑键盘,编码器像老式收音机的旋钮(左转/右转/按下)
    • 每个遥控器(输入设备)要指定控制哪个电视频道列表(组)
  3. ​默认组——出厂预设频道​

    • 按钮、滑块等控件出厂时自带"默认遥控列表"
    • 但需要我们先买个遥控器(创建组)并绑定这个默认列表才能使用

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():绑定遥控器和操作设备(如旋钮、键盘)
  • ​常见错误​​:
    如果没创建组就直接用,就像拿着没电池的遥控器狂按——系统不会有反应!

​总结​

  • ​自定义组​​适合需要​​精准控制​​的场景(比如游戏手柄只控制角色移动)。
  • ​默认组​​适合​​快速开发​​,系统帮你自动管理(比如简单菜单导航)。
  • 核心操作就三步:​​造遥控器 → 塞按钮 → 绑设备​​,其他都是系统帮你完成的“魔法”。
  1. ​关键按键——遥控器必备按键​

    • 方向键(←→↑↓):像游戏手柄方向键切换选项
    • 回车键(Enter):相当于"确认"按钮
    • 左右键(Next/Prev):频道切换键,快速跳转选项
    • 最重要且建议必要的按键是:
        LV_KEY_NEXT/PREV
        LV_KEY_ENTER
        LV_KEY_UP/DOWN/LEFT/RIGHT

      在我们回调函数 read_cb 中,应该优先考虑将现有的键对应转换为上面这些键,以便能在组中导航并与所选对象进行交互。

      其实一般来说,只使用 LV_KEY_LEFT/RIGHT 就足够了,因为大多数对象都可以通过它们被完全控制。

      对于编码器,默认只使用 LV_KEY_LEFTLV_KEY_RIGHT LV_KEY_ENTER

  2. ​编码器的两种模式——空调遥控器的两种状态​

    • 导航模式:旋钮左右转切换温度/风速选项(切换焦点)
    • 编辑模式:选中温度后,转动旋钮调整具体数值(修改数值)
    • 短按旋钮切换模式,就像空调遥控器上的"模式"键
    • 如果我们使用编码器,因为编码器的按键有限,所以在各个对象之间进行导航就不方便了。这个时候就需要区分模式了,lvgl帮我们区分了两种模式:编辑和导航模式

      在导航模式下,编码器的 LV_KEY_LEFT/RIGHT 被转换为 LV_KEY_NEXT/PREV,这样就可以通过转动编码器来选择下一个或上一个对象。

      如果需要修改对象的值,比如有个滑杆(lv_slider)代表音量或亮度,我们通过按下 LV_KEY_ENTER 将模式切换为编辑模式,这样我们可以通过转动编码器来修改滑杆的值。然后根据对象的类型,短按或长按 LV_KEY_ENTER 切换为导航模式(离开编辑模式)

  3. ​对象状态变化——手机APP的选中效果​

    • 聚焦状态:像选中微信图标时的蓝色高亮
    • 编辑状态:进入聊天框输入时,光标闪烁效果
    • 我们可以自定义这两种状态的显示样式
    • 如果通过触摸板点击选中对象或通过编码器或键盘聚焦对象,对象的状态会变为 LV_STATE_FOCUSED

      如果对象进入编辑模式,对象将进入 LV_STATE_FOCUSED | LV_STATE_EDITED 状态。

      所以,我们可以根据上面这两点,为对象设置被选中或聚焦或编辑时的样式。

  4. ​实现步骤(比喻版)​

    • ① 买新遥控器(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 预先埋入“感应线”​

基础对象在创建时,已经内置了​​三条关键逻辑线​​:

  1. ​状态线​​:LV_STATE_FOCUSED(聚焦状态)

    • 当物理按键导航或触摸选中时,自动激活该状态
    • 例如:用方向键切换按钮时,系统自动给当前按钮挂上“聚焦”标签
  2. ​样式线​​:LV_STYLE_...(视觉反馈规则)

    • 预设了聚焦时的边框加粗、背景色渐变等效果
    • 类似智能台灯的“有人靠近就亮暖光,离开切冷光”的规则
  3. ​事件线​​:lv_event_cb(动作反馈机制)

    • 当检测到点击/触摸事件时,自动调用注册的回调函数
    • 例如按钮被按下时播放“咔哒”音效(通过回调函数实现)
​2.2 继承机制:子控件“免配置”​

当开发者创建按钮、滑块等子控件时:

  • ​自动继承​​父级基础对象的所有能力(如同新生儿继承父母的基因)
  • ​无需重复编程​​:不用再写聚焦检测、样式切换等底层代码

​3. 生活场景类比​

想象用遥控器操作智能电视菜单:

  1. ​基础对象 = 电视系统内核​
    • 预装所有菜单项的导航规则(上下键切换、确认键选中)
  2. ​子控件 = 具体菜单项​
    • 每个菜单项只需关注自己的功能(如“设置”打开设置页)
    • ​无需关心​​“如何被选中”“选中后怎么高亮”——系统内核已自动处理

​4. 技术实现揭秘​

通过三个关键设计实现自动化响应:

  1. ​状态机机制​
    // 系统内部自动更新对象状态(伪代码)
    if (按键方向右按下) {
       当前聚焦对象.state = LV_STATE_FOCUSED;  // 去掉旧焦点
       右侧对象.state = LV_STATE_FOCUSED;     // 设置新焦点
    } [3,5](@ref)
  2. ​样式级联​
    // 基础对象预定义的聚焦样式
    lv_style_set_border_color(&focus_style, LV_COLOR_RED); // 聚焦时红边框[5](@ref)
  3. ​事件冒泡​
    // 触摸事件从子对象向父对象传递(如点击按钮触发屏幕刷新)
    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(父容器); // 类似把开关装进一个盒子里

三、如何给开关"化妆"?

可以分别给三个部分设置颜色、大小:

  1. ​改背景颜色​​:

    lv_obj_set_style_bg_color(sw, lv_color_hex(0xc43e1c), LV_PART_MAIN); // 设置成番茄红
  2. ​调整指示器大小​​:

    lv_obj_set_style_pad_left(sw, 5, LV_PART_MAIN); // 左边缩进5像素,指示器变窄
  3. ​改旋钮样式​​:

    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);

  1. ​改背景颜色和间距​​(比如让文字离勾选框远点):

    // 背景颜色(整个复选框的底色)
    lv_obj_set_style_bg_color(cb, 颜色代码, LV_PART_MAIN);
    // 调整勾选框和文字的距离(数字越大离得越远)
    lv_obj_set_style_pad_column(cb, 20, LV_PART_MAIN);
  2. ​改勾选框的样式​​:

    // 未勾选时的颜色(比如紫色)
    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);

​四、控制复选框的状态​

  1. ​判断是否勾选​​:

    if(lv_obj_has_state(cb, LV_STATE_CHECKED)) {
        // 打钩了,做点事情
    }
  2. ​手动勾选/取消​​:

    lv_obj_add_state(cb, LV_STATE_CHECKED);    // 强制打钩
    lv_obj_clear_state(cb, LV_STATE_CHECKED);  // 取消打钩
  3. ​禁用复选框​​(变灰不能点):

    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("取消勾选啦~");
    }
}

​六、用键盘/按键控制​
如果你的设备有物理按键:

  • 按上/右键:勾选
  • 按下/左键:取消勾选
  • 按回车键:切换勾选状态

​七、避坑指南​

  1. 修改样式时注意区分 LV_PART_MAIN(整体背景)和 LV_PART_INDICATOR(小方框)
  2. 禁用状态用的是 LV_STATE_DISABLED,恢复用 lv_obj_clear_state
  3. 文档里有个小笔误:最后恢复禁用状态时应该是 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!");
    }
}

五、关键设计思想

  1. ​事件驱动模型​​:LVGL 通过事件链表实现松耦合交互,用户只需关注注册回调。
  2. ​状态位管理​​:对象状态用二进制位存储,高效检测(如 LV_STATE_CHECKED 对应一个位)。
  3. ​事件冒泡​​(高级特性):事件可向父对象传递,源码中会有更复杂的链表遍历逻辑。

通过这种机制,LVGL 可以高效处理用户交互事件,同时保持代码的灵活性和可扩展性。

3-7_滚轮(lv_roller)

一、滚轮是啥?

​滚轮​​就是一个可以上下滑动选择选项的控件,类似你手机设置时间时那个转动的列表。比如选年月日、选模式、选菜单项时都很好用。


二、它能干啥?

  1. ​显示多个选项​
    你可以给它一堆文字选项(比如"苹果、香蕉、橘子"),它会自动排列成一个列表。

  2. ​滑动选择​
    用户上下滑动(或拖动)就能选择不同的选项,手感流畅。

  3. ​自动居中​
    选中的选项会自动“吸”到中间位置(默认行为,可改)。

  4. ​事件反馈​
    比如选完后可以触发一个事件(比如更新屏幕上的其他内容)。


三、怎么配置它?

  1. ​设置选项​
    用字符串数组表示选项,用 \n 分隔,比如:

    lv_roller_set_options(roller, "苹果\n香蕉\n橘子", LV_ROLLER_MODE_NORMAL);
  2. ​外观调整​

    • ​文字大小​​:调大调小字体。
    • ​颜色​​:改文字颜色、背景色。
    • ​动画时间​​:滑动时的动画快慢(比如设置 300ms 更顺滑)。
  3. ​方向和对齐​

    • 可以改成水平滚动(默认是垂直)。
    • 选项左对齐、居中还是右对齐。
  4. ​循环滚动​
    开启后,滑到最后一个选项会跳回第一个,无限循环(比如选月份时)。


四、怎么知道用户选了啥?

  1. ​获取选中项​
    用 lv_roller_get_selected(roller) 拿到选项的索引(比如0是第一个,1是第二个)。

  2. ​绑定事件​
    当用户选完时,可以触发回调函数(比如更新一个标签显示当前选中的内容):

    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的滑动条​​ 就是让你在屏幕上做一个类似的拖拽控件,用来调节数值(比如亮度、音量、进度等)。


二、滑动条怎么用?三步搞定!

  1. ​创建滑动条​​(就像在屏幕上放个空白条):

    lv_obj_t *slider = lv_slider_create(lv_scr_act());  // 创建一个滑动条,放在当前屏幕
  2. ​设置基本属性​​:

    • ​方向​​:横着还是竖着?
      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
  3. ​加点样式​​(让它变漂亮):

    • ​轨道颜色​​(滑动的背景条):
      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);
}

四、小技巧和常见坑

  1. ​显示数值​​:滑动条本身不显示数字,你可以在旁边放个标签(lv_label),在事件回调中更新它。
  2. ​动画效果​​:设置值时加上动画更丝滑:
    lv_slider_set_value(slider, 80, LV_ANIM_ON);  // 滑块会慢慢滑到80
  3. ​垂直滑动条​​:设置高度大于宽度,自动变垂直!
  4. ​取值范围不对​​:记得先设范围再设值,否则可能无效。

五、总结比喻

  • ​创建滑动条​​ → 买了个空白音量条
  • ​设置范围​​ → 贴个标签写“最低静音,最高震耳”
  • ​设置样式​​ → 把灰色轨道涂绿,滑块涂红
  • ​事件回调​​ → 耳朵贴着喇叭,一拖动就听到声音变化

这样是不是超清楚?试着代码跑一跑,调个颜色或数值,立刻有成就感!

3-10_圆弧(lv_arc)

1. ​​什么是圆弧(Arc)?​

  • ​简单理解​​:圆弧是一个可以显示弧形进度或角度的部件,比如钟表的表盘、音量控制旋钮、加载进度条等。
  • ​在LVGL中​​:lv_arc 是LVGL提供的一个基础部件,可以通过代码控制它的起始角度、结束角度、颜色、样式等。

2. ​​关键参数​

  • ​角度范围​​:
    • 默认角度范围是 (类似钟表的3点方向)到 360°(一圈)。
    • 课程中提到的 0°/360°90°(12点方向)、180°(9点方向)、270°(6点方向)是常见角度参考点。
  • ​起始角度和结束角度​​:
    • 通过设置起始角度(start_angle)和结束角度(end_angle),可以控制弧形的显示范围。
    • 例如:设置 start_angle=90end_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控件就像一支智能画笔,可以轻松画出各种线条和形状。以下是通俗易懂的解释:

​功能简介​

  • ​画线小能手​​:它能根据你设定的多个坐标点,自动连接成直线或折线(比如画三角形、多边形)。
  • ​灵活调整​​:线条颜色、粗细、端点形状都能自定义,还支持动态变化(如让线条动起来)。

​使用三步曲​

  1. ​创建画布​​:

    lv_obj_t *line = lv_line_create(parent);  // 在父容器上创建线条控件
  2. ​设置坐标点​​:
    定义一个点数组(每个点包含x/y坐标),例如画一个"V"字形:

    static lv_point_t points[] = {{0, 0}, {50, 100}, {100, 0}}; // 三个点坐标
    lv_line_set_points(line, points, 3);  // 绑定到线条控件
  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);
}
  • ​形象比喻​​:就像有只无形的手在上下拖动线条的第二个点,每拖动一次就刷新画面

​三、代码运行流程​

  1. ​初始化动画​​ → 买录像带、贴标签
  2. ​设置动画参数​​ → 录制备忘录(怎么动、动多久)
  3. ​启动动画​​ → 按下播放键
  4. ​每次动画更新​​ → 调用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


​五、举个实际例子​

想象你正在做​​心电图波形动画​​:

  1. 定义一个心电图线条的多个点
  2. 用类似代码动态修改某个关键点的Y坐标
  3. 就会看到心电图波形有规律地起伏!

​常见用途​

  • 绘制图表折线
  • 创建自定义图形边框
  • 实现进度条、波浪动画等动态效果

​小贴士​

  • 点坐标默认为相对父容器的位置,也可通过lv_obj_set_align()调整基准点。
  • 通过lv_style_set_line_dash_width(&style, 10)可画出虚线效果。

总结:lv_line把复杂的绘图简化为设置点坐标+调整样式,像连点游戏一样简单有趣!

LVGL 的文本框(lv_textarea)可以理解为手机上的"输入框",用来实现用户输入文字、密码或显示多行文本。以下是通俗易懂的讲解:

3-13_文本框(lv_textarea)


📝 基础概念

  1. ​文字容器​​:像一个透明盒子,可以显示文字、支持键盘输入
  2. ​功能多样​​:
    • 支持单行/多行模式(如聊天输入框)
    • 密码模式(显示​**​*)
    • 添加提示文字(如"请输入手机号")
    • 限制输入内容(如只允许数字)

🔧 常用操作(比喻为手机操作)

  1. ​创建文本框​​:lv_textarea_create(parent)

    • 相当于在屏幕上新建一个空白输入框
  2. ​设置尺寸位置​​:

    lv_obj_set_size(textarea, 200, 60);  // 设置大小
    lv_obj_center(textarea);            // 居中显示
  3. ​添加文字​​:

    • 插入文字:lv_textarea_add_char(textarea, 'A')
    • 追加文字:lv_textarea_add_text(textarea, "Hello")
  4. ​密码模式​​:

    lv_textarea_set_password_mode(textarea, true); // 开启密码***
  5. ​提示文字​​:

    lv_textarea_set_placeholder_text(textarea, "点击输入...");

🎛️ 进阶功能

  1. ​输入限制​​:

    lv_textarea_set_accepted_chars(textarea, "0123456789"); // 只允许输入数字
  2. ​单行/多行切换​​:

    lv_textarea_set_one_line(textarea, true); // 强制单行(类似手机验证码输入框)
  3. ​事件处理​​:

    // 当内容变化时触发回调
    lv_obj_add_event_cb(textarea, text_changed_event, LV_EVENT_VALUE_CHANGED, NULL);

📌 使用步骤(以创建密码输入框为例)

  1. 创建基础文本框
  2. 设置尺寸和位置
  3. 开启密码模式
  4. 添加提示文字
  5. 限制输入为数字
  6. 添加内容变化监听

⚠️ 注意事项

  1. 需要配合键盘部件才能输入
  2. 多行模式下要手动处理滚动条
  3. 中文输入需要额外配置字库
  4. 长文本建议开启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)

一、按钮矩阵是什么?

​像乐高积木一样排列的按钮组​
可以一次性创建多个整齐排列的按钮(比如计算器键盘、菜单选项)。相比单独创建多个按钮,它更节省代码、布局更方便。


二、按钮矩阵的创建步骤

  1. ​定义按钮文字​
    用一个字符串数组表示每个按钮的文字,例如:

    static const char *btn_map[] = {"1", "2", "3", "\n", "4", "5", "6", ""};

    \n 表示换行,最后以空字符串 "" 结尾。

  2. ​创建按钮矩阵对象​

    lv_obj_t *btnm = lv_btnmatrix_create(lv_scr_act());
    lv_btnmatrix_set_map(btnm, btn_map); //绑定按钮文字
  3. ​调整大小和位置​
    可以手动设置大小,或根据内容自动调整:

    lv_obj_set_size(btnm, 200, 150);
    // 或
    lv_btnmatrix_set_btn_width(btnm, 0, 2); //第0列按钮占2个单位宽度

三、常用设置技巧

  1. ​按钮布局控制​

    • lv_btnmatrix_set_btn_ctrl_all(btnm, LV_BTNMATRIX_CTRL_CHECKABLE);
      让所有按钮可以被选中(类似开关效果)
    • lv_btnmatrix_set_one_checked(btnm, true);
      只允许一个按钮被选中(类似单选按钮)
  2. ​样式修改​
    修改按钮颜色、圆角等(示例):

    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);
  3. ​事件处理​
    监听按钮点击事件:

    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列的矩阵排列数字和运算符
  • ​菜单导航​​:纵向排列多个选项按钮
  • ​快捷工具栏​​:横向排列常用功能图标

五、避坑指南

  1. ​数组结尾必须加空字符串​​ "",否则会内存溢出!
  2. 按钮索引 ​​从0开始​​(第一行第一个是0,换行后继续递增)
  3. 中文显示需要先加载中文字库

通过这种方式,你可以快速创建整齐划一的按钮组,代码量减少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

  1. ​什么是图标字体?​
    就像我们电脑里的宋体、楷体一样,但字体中的每个"字符"其实是各种小图标(比如房子、箭头、WiFi符号)。这样做的好处是图标可以像文字一样随意缩放、变色,比图片更节省资源。

  2. ​为什么用FontAwesome?​

    • 这是全球最流行的免费图标库(有2000+精美图标)
    • 比如微信图标、下载箭头、音乐符号等应有尽有
    • 类似手机APP里的那些常用小图标都能找到
  3. ​添加步骤类比:​
    就像给手机安装新字体:

    • ① 下载字体文件(就像.ttf文件)
    • ② 把需要的图标"字符"导入LVGL
    • ③ 使用时就像打字一样输入特定符号就能显示图标
  4. ​必须注意的版权问题:​

    • 免费版(FontAwesome Free)可用于商业项目,但必须注明来源
    • 如果修改了图标,必须改名不能叫FontAwesome
    • 详细规则见课程提供的license链接
  5. ​实际开发流程:​

    • 官网选图标 → 获取对应Unicode编码 → 生成字体文件 → 集成到LVGL
    • 例如:选中"❤"图标,它的Unicode是U+2764,代码中写LV_SYMBOL_HEART就能显示
  6. ​为什么需要文档链接?​

    • 中文文档:方便英语不好的开发者
    • 英文文档:获取最新版本的功能说明
    • 官方论坛:遇到问题时可以查询类似案例
  7. ​学习建议:​

    • 先尝试添加现成的FontAwesome图标
    • 熟练后再学习制作自己的图标字体
    • 遇到问题优先查阅提供的文档链接

​举个例子:​​ 想做一个音乐播放器界面,可以直接使用FontAwesome的播放按钮图标(▶),暂停图标(⏸),音量图标(🔈),无需自己画图,直接通过字体调用即可。

后续学习中如果遇到具体操作问题,可以重点参考文档中的代码示例和配置说明。记住图标字体是嵌入式开发中优化UI资源占用的重要手段哦!

### 如何在LVGL库中使用Groups #### 创建Group对象 为了管理多个输入设备并使它们能够协同工作,可以创建一个`lv_group_t`类型的group对象。这通过调用函数`lv_group_create()`实现[^4]。 ```c lv_group_t * group; group = lv_group_create(); ``` #### 添加对象到Group 一旦有了group实例,就可以向其中添加控件(widgets)。任何被加入group的对象都会成为焦点循环的一部分,在不同输入设备间导航时会按照添加顺序依次获得焦点。 ```c /*假设已经存在两个按钮btn1和btn2*/ lv_group_add_obj(group, btn1); lv_group_add_obj(group, btn2); ``` #### 设置默认聚焦方向 当在一个group内移动焦点时,默认情况下是从上至下、从左往右排列的对象将会按此顺序接收焦点。可以通过设置属性来改变这种行为模式: ```c // 将组的方向设为双向水平或垂直滚动 lv_group_set_focus_cb(group, my_custom_focus_move_event); ``` 这里`my_custom_focus_move_event`是一个自定义回调函数名,用于处理特定场景下的焦点转移逻辑[^4]。 #### 关联Input Device与Group 为了让某个输入设备能控制指定的object group,需要将其绑定在一起。通常是在初始化阶段完成这项配置。 ```c static void input_device_init(void){ /*省略其他代码...*/ // 假定input_dev是指向已注册好的输入驱动结构体指针 lv_indev_set_group(input_dev, group); /*省略其他代码...*/ } ``` 以上就是关于如何利用LVGL中的groups特性的一些基本介绍以及简单示范。希望这些信息可以帮助理解这一机制的工作原理及其应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值