【LVGL】lv_label 标签:从基础显示到多语言UI实战
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不仅仅是能显示文字更是要让信息在不同场景、对不同用户都清晰、友好、易于交互。

相关新闻

Flutter 组件 tw_queue 的适配 鸿蒙Harmony 实战 - 驾驭分布式高并发任务队列、实现鸿蒙端流式任务调度与生产级持久化断点续传方案

Flutter 组件 tw_queue 的适配 鸿蒙Harmony 实战 - 驾驭分布式高并发任务队列、实现鸿蒙端流式任务调度与生产级持久化断点续传方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 组件 tw_queue 的适配 鸿蒙Harmony 实战 - 驾驭分布式高并发任务队列、实现鸿蒙端流式任务调度与生产级持久化断点续传方案 前言 在鸿蒙(OpenHarmony)生态…

2026/7/5 13:43:10 阅读更多 →
Qwen3-32B性能优化指南:如何用更少资源获得更快推理速度?

Qwen3-32B性能优化指南:如何用更少资源获得更快推理速度?

Qwen3-32B性能优化指南:如何用更少资源获得更快推理速度? 在AI模型部署的实战中,我们常常面临一个两难选择:想要强大的模型能力,就得准备好昂贵的算力资源;想要控制成本,又担心模型性能不达标。…

2026/7/3 15:13:02 阅读更多 →
AD高效PCB布线:快捷键与实用技巧全解析

AD高效PCB布线:快捷键与实用技巧全解析

1. 布线效率的基石:你必须掌握的AD核心快捷键 干了这么多年硬件设计,我最大的感受就是,高手和菜鸟的区别,往往不在于谁的理论更扎实,而在于谁的操作更“溜”。在Altium Designer(我们习惯叫AD)里…

2026/5/17 2:22:58 阅读更多 →

最新新闻

行业领先·审查通过·高性能|运营商行业数据库审计和监测最佳实践指南

行业领先·审查通过·高性能|运营商行业数据库审计和监测最佳实践指南

一、方案概要:数据化落地的全周期数据库安全治理体系【提示】本段立足运营商数字化转型全局,聚焦产品核心特性与落地成效,系统性概述方案核心价值与行业定位。在数字基建升级与数据合规强监管态势下,电信运营商数据库安全治理成为…

2026/7/5 13:42:12 阅读更多 →
踩坑3周,我在实验室内网搭了个零公网请求的论文AIGC筛查本地系统

踩坑3周,我在实验室内网搭了个零公网请求的论文AIGC筛查本地系统

搞AIGC内容本地筛查的这三周我人都麻了,之前先后试了GPTZero、Originality.ai、团象AIGC检测、Crossplag、Copyscape、PaperPass旗下的AI检测,全不好用。这些工具要么强制要求把全文上传公网服务器,要么对理工科论文的公式部分误判率高到离谱…

2026/7/5 13:42:12 阅读更多 →
11、<简单>有一个六位数,其个位数字7,现将个位数字移至首位(十万位),而其余各位数字顺序不变,均后退一位,得到一个新的六位数,假如新数为I旧数的4倍,求原来的六位数

11、<简单>有一个六位数,其个位数字7,现将个位数字移至首位(十万位),而其余各位数字顺序不变,均后退一位,得到一个新的六位数,假如新数为I旧数的4倍,求原来的六位数

#include <iostream> using namespace std;int main() {// old 是原六位数&#xff0c;个位固定为7for (long old 100007; old < 999997; old 10){// 拆分前5位long front old / 10;// 个位7移到十万位&#xff0c;生成新六位数long newNum 700000 front;// 判断…

2026/7/5 13:40:12 阅读更多 →
终极精简指南:使用PowerShell脚本让Windows 11瘦身50%

终极精简指南:使用PowerShell脚本让Windows 11瘦身50%

终极精简指南&#xff1a;使用PowerShell脚本让Windows 11瘦身50% 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 你是否曾为Windows 11那臃肿的系统体积和缓慢的…

2026/7/5 13:40:12 阅读更多 →
从《中国统计年鉴》到可比数据:手把手教你计算不变价GDP

从《中国统计年鉴》到可比数据:手把手教你计算不变价GDP

1. 为什么需要计算不变价GDP&#xff1f; 我第一次接触GDP数据时&#xff0c;发现一个奇怪现象&#xff1a;某城市2000年GDP是1000亿元&#xff0c;2020年GDP是8000亿元&#xff0c;看起来增长了8倍。但老师告诉我&#xff0c;这个比较毫无意义&#xff0c;因为没考虑物价变化。…

2026/7/5 13:40:12 阅读更多 →
编程启蒙|Scratch 转 Python 系列第 3 天完整教程

编程启蒙|Scratch 转 Python 系列第 3 天完整教程

本篇是零基础 Python 自学系列 Scratch 转 Python 第 3 天笔记&#xff0c;适合纯小白入门&#xff0c;内容包含实操代码、详细讲解与配套练习题&#xff0c;全程 Scratch 积木代码 Python 双向对照教学。 一、昨日内容复盘&#xff08;Scratch 转 Python Day2 for 循环与 ra…

2026/7/5 13:36:11 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools&#xff1a;5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里&#xff0c;参与了关于混合后量子密码学的讨论&#xff0c;应付端点攻击找茬的人&#xff0c;还参与留言板讨论后&#xff0c;发现“威胁模型”对多数人仍是陌生概念&#xff0c;且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”&#xff1a;我理解的渗透测试到底是什么&#xff1f;每次看到新闻里说某个大公司的数据被“黑”了&#xff0c;或者某个网站被攻击导致服务瘫痪&#xff0c;你是不是和我一样&#xff0c;心里会冒出两个念头&#xff1a;一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools&#xff1a;5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里&#xff0c;参与了关于混合后量子密码学的讨论&#xff0c;应付端点攻击找茬的人&#xff0c;还参与留言板讨论后&#xff0c;发现“威胁模型”对多数人仍是陌生概念&#xff0c;且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”&#xff1a;我理解的渗透测试到底是什么&#xff1f;每次看到新闻里说某个大公司的数据被“黑”了&#xff0c;或者某个网站被攻击导致服务瘫痪&#xff0c;你是不是和我一样&#xff0c;心里会冒出两个念头&#xff1a;一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻