4、lv_label与文本显示:创建标签、设置文本、动态更新文本、文本对齐与长文本处理
标签(Label)是 LVGL 里最基础、最常用的控件。说白了,它就是用来显示文字的。你做的任何界面,几乎都离不开它。这一章我们就把它彻底搞明白。
核心要点:lv_label 是 LVGL 文本显示的基石。掌握它的创建、更新、对齐和长文本处理,你就能应对 90% 的文本显示需求。
4.1 创建你的第一个标签
创建标签非常简单。我刚开始用 LVGL 时,第一件事就是写个 "Hello World" 看看效果。代码就几行:
/* 创建一个标签,父对象是当前活动屏幕 */
lv_obj_t * label = lv_label_create(lv_scr_act());
/* 设置文本内容 */
lv_label_set_text(label, "Hello LVGL!");
/* 让标签居中显示 */
lv_obj_center(label);
嗯,这里要注意一点:lv_obj_center() 是 LVGL v8 之后才有的便捷函数。如果你还在用 v7,得手动设置坐标。我个人习惯在创建控件后立刻设置对齐方式,这样后面调整位置心里有数。
4.2 设置与修改文本
LVGL 提供了好几种设置文本的方式。我挑最常用的几个说说:
| 函数 | 说明 | 适用场景 |
|---|---|---|
lv_label_set_text() |
直接设置静态字符串 | 固定文本、标题 |
lv_label_set_text_fmt() |
格式化字符串(类似 printf) | 显示变量、数值 |
lv_label_set_text_static() |
使用静态内存,不复制字符串 | 节省 RAM,只读文本 |
你想想看,如果只是显示一个固定标题,用 lv_label_set_text() 就够了。但如果你要显示温度、电压这种实时数据,lv_label_set_text_fmt() 就派上用场了:
float temperature = 25.6;
lv_label_set_text_fmt(label, "当前温度: %.1f°C", temperature);
我在项目中遇到过一个问题:频繁调用 lv_label_set_text() 更新数据时,内存碎片越来越严重。后来发现,如果文本长度变化不大,可以用 lv_label_set_text_static() 配合全局缓冲区,避免动态内存分配。这个坑,踩过一次就记住了。
4.3 动态更新文本
动态更新文本是嵌入式 UI 的常态。比如显示倒计时、传感器读数、网络状态等。LVGL 处理这个非常高效。
核心思路就三步:
- 创建一个标签
- 在定时器或事件回调中修改文本
- LVGL 自动刷新显示
来看一个实际例子:
/* 创建标签 */
lv_obj_t * counter_label = lv_label_create(lv_scr_act());
lv_obj_set_pos(counter_label, 10, 10);
/* 定时器回调:每秒更新一次 */
static int count = 0;
void timer_cb(lv_timer_t * timer) {
count++;
lv_label_set_text_fmt(counter_label, "计数: %d", count);
}
/* 创建定时器,周期 1000ms */
lv_timer_t * my_timer = lv_timer_create(timer_cb, 1000, NULL);
小技巧:如果更新频率很高(比如 50ms 一次),建议用 lv_label_set_text_static() 配合预分配缓冲区,避免频繁 malloc/free。我曾经在一个 60fps 的仪表盘项目里,就因为没注意这个,导致系统跑几分钟就卡死。
4.4 文本对齐
文本对齐看似简单,但实际项目中经常搞错。LVGL 的对齐分两个层面:
- 标签本身的对齐:标签对象在父容器中的位置
- 文本在标签内的对齐:文字在标签区域内的排列方式
先说标签对齐。用 lv_obj_align() 可以精确控制:
/* 左上角对齐 */
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10);
/* 底部居中 */
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -10);
再说文本在标签内的对齐。这个用 lv_label_set_long_mode() 配合样式来控制:
/* 设置文本在标签内居中 */
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
| 对齐模式 | 宏定义 | 效果 |
|---|---|---|
| 左对齐 | LV_TEXT_ALIGN_LEFT |
默认,文字靠左 |
| 居中对齐 | LV_TEXT_ALIGN_CENTER |
文字水平居中 |
| 右对齐 | LV_TEXT_ALIGN_RIGHT |
文字靠右 |
| 自动 | LV_TEXT_ALIGN_AUTO |
根据文本方向自动选择 |
注意:文本对齐只在标签的宽度大于文本实际宽度时才有明显效果。如果标签宽度刚好包裹文本,对齐就没意义了。我见过有人折腾半天对齐没反应,最后发现是标签宽度设成了 LV_SIZE_CONTENT。
4.5 长文本处理
这是实际项目中最容易出问题的地方。屏幕就那么点大,文本一长,要么显示不全,要么布局乱掉。LVGL 提供了几种长文本模式:
/* 设置长文本模式 */
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); /* 自动换行 */
lv_label_set_long_mode(label, LV_LABEL_LONG_DOT); /* 超出显示... */
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL); /* 滚动显示 */
lv_label_set_long_mode(label, LV_LABEL_LONG_CLIP); /* 裁剪超出部分 */
| 模式 | 宏定义 | 行为 | 典型场景 |
|---|---|---|---|
| 自动换行 | LV_LABEL_LONG_WRAP |
文本自动换到下一行 | 多行文本、段落 |
| 省略号 | LV_LABEL_LONG_DOT |
超出部分显示为 ... | 列表项、文件名 |
| 滚动 | LV_LABEL_LONG_SCROLL |
文本循环滚动显示 | 跑马灯、通知栏 |
| 裁剪 | LV_LABEL_LONG_CLIP |
直接裁剪超出部分 | 固定区域显示 |
我个人最常用的是 WRAP 和 DOT。比如显示一条消息详情,用 WRAP 让用户能看完所有内容。而显示文件列表时,用 DOT 保持每行高度一致,界面更整齐。
这里有个坑:使用 LV_LABEL_LONG_SCROLL 时,滚动动画默认是无限循环的。如果你只想滚动一次就停,需要额外处理。我曾经在一个车载项目里,用滚动模式显示警告信息,结果文字一直滚,司机说看着头晕……后来改成滚动一次加停顿,才解决问题。
4.6 本章知识体系
下面这张图帮你理清 lv_label 的核心操作脉络:
4.7 综合示例:一个简单的信息面板
最后,我把这章的知识点串起来,写一个实际可用的信息面板。它显示设备名称、运行时间、状态和一条滚动通知:
/* 创建信息面板 */
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, 300, 200);
lv_obj_center(panel);
/* 设备名称 - 静态文本,居中 */
lv_obj_t * title = lv_label_create(panel);
lv_label_set_text(title, "智能传感器 v2.0");
lv_obj_set_style_text_align(title, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_width(title, 280);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
/* 运行时间 - 动态更新 */
lv_obj_t * runtime = lv_label_create(panel);
lv_obj_align(runtime, LV_ALIGN_TOP_LEFT, 10, 50);
lv_label_set_text(runtime, "运行时间: 0s");
/* 状态指示 - 带省略号 */
lv_obj_t * status = lv_label_create(panel);
lv_obj_align(status, LV_ALIGN_TOP_LEFT, 10, 80);
lv_label_set_long_mode(status, LV_LABEL_LONG_DOT);
lv_obj_set_width(status, 280);
lv_label_set_text(status, "状态: 系统初始化完成,等待传感器数据...");
/* 滚动通知 - 跑马灯效果 */
lv_obj_t * notice = lv_label_create(panel);
lv_obj_align(notice, LV_ALIGN_BOTTOM_LEFT, 10, -10);
lv_label_set_long_mode(notice, LV_LABEL_LONG_SCROLL);
lv_obj_set_width(notice, 280);
lv_label_set_text(notice, "通知: 固件升级可用,请连接网络后更新。");
/* 定时器更新运行时间 */
static uint32_t seconds = 0;
void update_runtime(lv_timer_t * t) {
seconds++;
lv_label_set_text_fmt(runtime, "运行时间: %d s", seconds);
}
lv_timer_create(update_runtime, 1000, NULL);
这个例子涵盖了本章所有核心操作。你把它跑起来,就能看到标签的创建、对齐、动态更新、省略号和滚动效果。嗯,自己动手试一遍,比看十遍文档都管用。