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 处理这个非常高效。

核心思路就三步:

  1. 创建一个标签
  2. 在定时器或事件回调中修改文本
  3. 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 的核心操作脉络:

lv_label 文本显示知识体系 lv_label 文本显示核心知识体系 lv_label 控件 创建与设置文本 动态更新文本 文本对齐 长文本处理 lv_label_create set_text / set_text_fmt 定时器 + 格式化更新 lv_obj_align / text_align WRAP / DOT / SCROLL / CLIP 避坑总结 • 频繁更新文本时,优先用 set_text_static + 预分配缓冲区 • 长文本模式要配合标签宽度设置,否则效果出不来

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

这个例子涵盖了本章所有核心操作。你把它跑起来,就能看到标签的创建、对齐、动态更新、省略号和滚动效果。嗯,自己动手试一遍,比看十遍文档都管用。