1. 从“Hello World”到“你好世界”lv_label 基础入门如果你刚开始接触LVGL想在屏幕上显示点文字那lv_label就是你第一个要打交道的对象。它简单到几乎不需要任何配置就能把一串字符扔到屏幕上。但就像学写字从描红到创作书法lv_label的深度远超你的想象。今天我就带你从最基础的显示开始一步步把它玩出花来特别是搞定让很多新手头疼的中文显示和多语言界面。先来点最直接的。在LVGL里创建一个标签简单到只需要一行代码lv_obj_t *my_label lv_label_create(lv_scr_act());这行代码执行后屏幕上其实还看不到任何东西因为它只是个“空壳”。你需要用lv_label_set_text给它注入灵魂lv_label_set_text(my_label, Hello, LVGL!);这时候屏幕上应该就会出现这行文字了。但位置可能有点随机通常默认在左上角。为了让界面好看点我们得学会控制它。这里有个新手特别容易混淆的概念标签对象的大小和字体的大小完全是两码事。你可以把标签想象成一个相框字体大小是照片里人的实际尺寸而相框的大小只是决定了展示照片的区域。你用一个巨大的相框装一张一寸证件照照片不会变大只会显得孤零零的反之用小相框装集体照照片就会被裁剪。所以设置标签的位置和宽高用的是lv_obj_set_pos和lv_obj_set_sizelv_obj_set_size(my_label, 200, 50); // 设置标签区域宽200像素高50像素 lv_obj_align(my_label, LV_ALIGN_CENTER, 0, 0); // 将标签对齐到屏幕正中央好了一个居中的标签有了。但你会发现这“Hello, LVGL!”的字体可能有点小或者不是你想要的风格。这就引出了下一个关键点样式和字体。在LVGL里几乎所有视觉相关的属性比如字体、颜色、边距都是通过“样式”来管理的。你可以创建一个样式把字体设置好然后把这个样式应用到标签上。默认情况下LVGL自带了一系列名为lv_font_montserrat_xx的字体xx代表像素高度比如lv_font_montserrat_16就是16像素高的英文字体。static lv_style_t my_style; // 声明一个样式变量 lv_style_init(my_style); // 初始化这个样式 lv_style_set_text_font(my_style, lv_font_montserrat_20); // 设置字体为20像素的Montserrat lv_style_set_text_color(my_style, lv_color_hex(0x333333)); // 设置字体颜色为深灰色 lv_obj_add_style(my_label, my_style, 0); // 将样式应用到标签对象上经过这番操作你的标签文字就变成了更大的深灰色字体。到这里你已经掌握了lv_label最核心的创建、设置文本、控制位置大小和更换字体的基本流程。但这仅仅是开始当你想显示更长的句子或者想把英文界面变成中文时挑战才真正开始。2. 当文字超出边框长文本处理的艺术在实际项目中你很少会只显示“Hello World”这么短的文本。更多时候是产品描述、状态提示、日志信息等一大段文字。在小屏幕的嵌入式设备上如何优雅地展示长文本直接影响了用户体验。LVGL的lv_label提供了几种强大的长文本模式这绝对是它的精华功能之一。通过lv_label_set_long_mode函数你可以告诉标签“当文字内容超出我给你的显示区域时你该怎么办” 它有几个选项我来给你挨个拆解一下并说说我实际用下来的感受。LV_LABEL_LONG_WRAP自动换行这是最常用、也最符合阅读习惯的模式。文字到达标签边界时会自动折到下一行。但这里有个坑换行是基于空格或标点等单词分隔符的。对于英文没问题但对于像中文这样没有空格分隔的语言如果刚好一个汉字在行末放不下LVGL取决于版本和配置可能会把这个汉字截断到下一行导致显示异常。我的经验是对于中文最好手动在需要换行的地方插入换行符\n或者确保标签宽度足够容纳一定数量的汉字。LV_LABEL_LONG_DOT末尾显示省略号当文字超出时在末尾显示“...”。这个模式非常适合显示标题、文件名等暗示后面还有内容。你需要通过lv_obj_set_width明确设置标签的宽度省略号才会在超过这个宽度时出现。**LV_LABEL_LONG_SCROLL滚动**和LV_LABEL_LONG_SCROLL_CIRCULAR循环滚动这两个是制作动态效果的利器。前者让文本单向滚动显示完全部内容后停止后者则会无限循环滚动就像新闻跑马灯。我做过一个智能家居中控屏用循环滚动来显示实时天气和通知效果非常棒。这里的关键点是滚动只在文本宽度超过标签宽度时才会触发。所以你需要精确控制标签的宽度。我踩过的坑是没设宽度或者设成了LV_SIZE_CONTENT结果文字根本不滚动排查了半天。让我们看一个综合性的例子假设我们要在宽度为200像素的区域里显示一条较长的状态信息并希望它循环滚动lv_obj_t *scroll_label lv_label_create(lv_scr_act()); lv_obj_set_width(scroll_label, 200); // **关键一步**设定滚动区域的宽度 lv_obj_align(scroll_label, LV_ALIGN_BOTTOM_MID, 0, -10); lv_label_set_long_mode(scroll_label, LV_LABEL_LONG_SCROLL_CIRCULAR); // 设置为循环滚动模式 lv_label_set_text(scroll_label, “系统启动完成当前时间2023-10-27温度25.6℃湿度60%”); // 可以设置滚动速度可选 lv_obj_set_style_anim_time(scroll_label, 5000, LV_PART_MAIN); // 5000ms完成一次完整滚动这个例子中如果文本的总像素宽度超过了200它就会开始从右向左循环滚动。lv_obj_set_style_anim_time控制了滚动一次完整周期的时间数值越大滚动越慢。在实际产品中这个功能常用于显示实时更新的、长度不定的信息既节省空间又吸引注意力。3. 攻克中文显示自定义字体实战详解默认的Montserrat字体很漂亮但它有个致命伤不支持中文。如果你的设备界面只需要英文那没问题。但要做面向国内用户的产品中文显示是绕不开的坎。网上很多教程提到自定义字体就一句话带过但这里面其实有不少细节和坑。我结合自己多次生成字体的经验给你捋一个清晰、可靠的流程。第一步准备字体文件你需要一个.ttf或.otf格式的中文字体文件。可以从一些开源字体网站下载比如思源黑体、站酷酷黑体等注意版权。选字体时要考虑嵌入式设备的屏幕分辨率和内存。屏幕小比如240x320就别用笔画太复杂的楷体清晰的黑体或宋体是更安全的选择。第二步使用LVGL官方字体转换工具这是最关键的一步。强烈推荐使用LVGL官方的在线转换工具Font Converter。把字体文件拖进去你会看到一堆选项。Range字符范围这是省内存的核心不要傻傻地勾选“All Characters”那会生成一个几十MB的巨大文件单片机根本装不下。你应该只添加你需要的字符。比如你的UI里只有“开始”、“停止”、“设置”、“温度”这些词那就只添加这些汉字和用到的标点。工具支持输入字符、Unicode范围、从文件读取等多种方式。我的习惯是先写一个包含所有UI文本的.txt文件用这个文件去生成最精准。Size字号设置你需要的像素高度。记住在嵌入式屏上16px、20px、24px是常用字号太小了看不清。BPP比特每像素推荐选1抗锯齿或2亚像素抗锯齿。1BPP足够清晰且体积小2BPP在彩色屏上边缘更平滑但体积会翻倍。Compression压缩可以勾选能有效减小生成的文件体积。第三步集成到工程点击Convert后会下载一个.c文件比如my_chinese_font_20.c。把这个文件放到你的LVGL工程目录下一定要记得把它加入编译比如在CMakeLists.txt或Makefile的源文件列表里加上它。否则链接时会报“未定义的引用”错误。第四步在代码中声明和使用在你要使用这个字体的C文件顶部声明它LV_FONT_DECLARE(my_chinese_font_20); // 声明字体名字和.c文件名一致然后在创建样式时指定这个字体static lv_style_t style_chinese; lv_style_init(style_chinese); lv_style_set_text_font(style_chinese, my_chinese_font_20); // 使用自定义中文字体 lv_obj_t *cn_label lv_label_create(lv_scr_act()); lv_obj_add_style(cn_label, style_chinese, 0); lv_label_set_text(cn_label, “温度传感器已连接”);如果一切顺利屏幕上就会正确显示中文了。如果显示乱码或方块请按以下顺序排查1. 字体文件是否成功加入编译。2. 代码中声明的字体变量名是否正确。3. 你显示的文字是否在生成字体时指定的字符范围内。4. 构建多语言UI一个完整的场景化案例掌握了基础显示、长文本处理和自定义字体我们就可以把这些知识串联起来打造一个真正实用的、支持多语言的嵌入式设备UI界面。假设我们要为一个智能温控器设计主界面需要显示温度、湿度、模式并且支持中英文切换。第一步设计UI布局与文本定义首先我们规划好屏幕上各个标签的位置。然后不要将文本直接硬编码在lv_label_set_text里而是应该定义多语言字符串表。这是实现多语言切换的核心。// 语言枚举 typedef enum { LANG_EN, LANG_CN } language_t; // 当前语言可保存在全局变量或非易失存储器中 static language_t current_lang LANG_CN; // 多语言字符串表 const char* str_temp[2] {Temperature: , 温度: }; const char* str_humidity[2] {Humidity: , 湿度: }; const char* str_mode_auto[2] {Auto, 自动}; const char* str_mode_manual[2] {Manual, 手动};第二步创建UI对象并应用样式我们创建多个标签并为可能显示中文的标签应用中文字体样式。// 声明和初始化中文字体样式假设已生成20px字体 LV_FONT_DECLARE(font_chinese_20); static lv_style_t style_label_chinese; lv_style_init(style_label_chinese); lv_style_set_text_font(style_label_chinese, font_chinese_20); lv_style_set_text_color(style_label_chinese, lv_color_black()); // 创建标签 lv_obj_t *label_title lv_label_create(lv_scr_act()); lv_obj_t *label_temp lv_label_create(lv_scr_act()); lv_obj_t *label_humidity lv_label_create(lv_scr_act()); lv_obj_t *label_mode lv_label_create(lv_scr_act()); lv_obj_t *label_value_temp lv_label_create(lv_scr_act()); // 用于显示温度数值 lv_obj_t *label_value_humidity lv_label_create(lv_scr_act()); // 用于显示湿度数值 // 对需要显示中文的静态标签应用中文字体样式 lv_obj_add_style(label_title, style_label_chinese, 0); lv_obj_add_style(label_temp, style_label_chinese, 0); lv_obj_add_style(label_humidity, style_label_chinese, 0); lv_obj_add_style(label_mode, style_label_chinese, 0); // 数值标签可以用默认英文字体因为只显示数字和符号 lv_obj_set_style_text_font(label_value_temp, lv_font_montserrat_24, 0); // 设置位置和对齐... lv_obj_align(label_title, LV_ALIGN_TOP_MID, 0, 20); lv_obj_align(label_temp, LV_ALIGN_LEFT_MID, 30, -40); lv_obj_align(label_value_temp, LV_ALIGN_LEFT_MID, 120, -40); // ... 其他对象布局第三步实现文本更新函数编写一个函数根据当前语言设置所有标签的文本。void update_ui_text() { int lang_idx (current_lang LANG_EN) ? 0 : 1; // 更新静态文本 lv_label_set_text(label_temp, str_temp[lang_idx]); lv_label_set_text(label_humidity, str_humidity[lang_idx]); // 动态文本如模式也需要更新 if (is_auto_mode()) { // 假设有一个函数判断当前模式 lv_label_set_text(label_mode, str_mode_auto[lang_idx]); } else { lv_label_set_text(label_mode, str_mode_manual[lang_idx]); } // 数值更新与语言无关但可以在这里一起更新 char buf[16]; snprintf(buf, sizeof(buf), %.1f°C, get_temperature()); // 获取温度值 lv_label_set_text(label_value_temp, buf); snprintf(buf, sizeof(buf), %d%%, get_humidity()); // 获取湿度值 lv_label_set_text(label_value_humidity, buf); }第四步处理长文本与交互假设设备名称很长比如“客厅智能温控器V2.0”我们可以在标题栏使用循环滚动效果。同时添加一个按钮用于切换语言。// 设置标题长文本和滚动模式 lv_label_set_long_mode(label_title, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_obj_set_width(label_title, 200); // 限制滚动区域宽度 const char* title[2] {Living Room Thermostat V2.0, 客厅智能温控器 V2.0}; lv_label_set_text(label_title, title[lang_idx]); // 创建语言切换按钮 lv_obj_t *btn_lang lv_btn_create(lv_scr_act()); lv_obj_align(btn_lang, LV_ALIGN_TOP_RIGHT, -10, 10); lv_obj_t *label_btn lv_label_create(btn_lang); lv_label_set_text(label_btn, EN/中); // 按钮本身文本固定 // 为按钮添加事件回调 lv_obj_add_event_cb(btn_lang, lang_btn_event_handler, LV_EVENT_CLICKED, NULL); // 事件处理函数 static void lang_btn_event_handler(lv_event_t * e) { lv_event_code_t code lv_event_get_code(e); if(code LV_EVENT_CLICKED) { // 切换语言 current_lang (current_lang LANG_EN) ? LANG_CN : LANG_EN; // 更新整个UI的文本 update_ui_text(); // 标题文本也需要根据新语言更新 int lang_idx (current_lang LANG_EN) ? 0 : 1; lv_label_set_text(label_title, title[lang_idx]); } }通过这个完整的案例你将lv_label的文本显示、长文本滚动、自定义字体、样式应用以及事件驱动UI更新全部串联了起来。在实际开发中你可能还需要将语言设置保存到Flash开机时读取。对于更复杂的多语言系统可以考虑使用类似gettext的键值对方式来管理字符串但上面这种数组索引的方式对于嵌入式设备来说通常已经足够高效和清晰。记住好的UI不仅仅是能显示文字更是要让信息在不同场景、对不同用户都清晰、友好、易于交互。