LVGL部件应用笔记(基于正点原子教程,持续更新)

前言

本文是基于正点原子教程的LVGL部件使用笔记,原视频链接如下

【正点原子】手把手教你学LVGL图形界面编程【真人出镜】LVGL图形界面编程视频教程 单片机 嵌入式_哔哩哔哩_bilibili

因为本文是针对部件层,想对于每个部件留下一点记录,不涉及具体芯片的移植,关于移植,其实很看具体情况,首先配置显示,触摸可选,然后提供时基。然后看项目是否要上RTOS,是否有外部RAM,是否有自研的管理算法...

本文使用Windows模拟器开发,使用版本为LVGL8.3,具体模拟器配置可看这篇文章

LVGL在VScode中安装模拟器运行配置笔记教程_vscode lvgl-CSDN博客

同时这里也可以使用Trae和Qcode,直接问AI,问题解决更快。

更新日志

首次创建并发布博客------260417


在开始之前需要新建一个mygui的c文件,h文件,并在main.c的while循环前调用函数MyGui;

cpp 复制代码
#include "lvgl/lvgl.h"
#include "mygui.h"

void MyGui(void)
{}

部件的基本属性

大小

C语言中没有"类"的概念,LVGL以结构体的形式来实现"类"的思维。这里先创建一个对象,显示效果为左图,当我们使用函数去定义这个对象的大小时,在下方加入一行代码,比如
lv_obj_set_size(obj, 320, 240); 显示的对象画布大小就会改变

cpp 复制代码
void MyGui(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
}


API函数:

cpp 复制代码
设置宽度:lv_obj_set_width(obj, new_width);
设置高度:lv_obj_set_height(obj, new_height);
同时设置宽度、高度:lv_obj_set_size(obj, new_width, new_height);

位置

这个概念也好理解,需要注意的是设置部件位置时,坐标原点在父对象的左上角

cpp 复制代码
void MyGui(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 50, 50);
    lv_obj_set_pos(obj, 100, 100);
}

API函数:

cpp 复制代码
设置X轴坐标:lv_obj_set_x(obj, new_x);
设置Y轴坐标:lv_obj_set_y(obj, new_y);
同时设置X、Y轴坐标:lv_obj_set_pos(obj, new_x, new_y);

对齐

对齐方式有两种,一种是参照父对象对齐-左图,另一种是参照其他对象对齐(无父子关系)-右图

cpp 复制代码
void MyGui(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 150, 150);
    lv_obj_set_pos(obj, 50, 50);
    lv_obj_t * son = lv_obj_create(obj);
    lv_obj_set_size(son, 50, 50);
    lv_obj_set_align(son, LV_ALIGN_CENTER);
}
cpp 复制代码
void MyGui(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 150, 150);
    lv_obj_set_pos(obj, 50, 50);
    lv_obj_t * obj2 = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj2, 50, 50);
    lv_obj_align_to(obj2, obj, LV_ALIGN_CENTER, 100, 0);
}

对齐位置有很多,这里用正点原子的图片用作参考


API:

cpp 复制代码
参照父对象对齐:lv_obj_set_align(obj, LV_ALIGN_...);
参照父对象对齐,再进行偏移:lv_obj_align(obj, LV_ALIGN_..., x, y);
参照其他对象对齐(无父子关系),再进行偏移:lv_obj_align_to(obj_to_align, obj_referece, LV_ALIGN_..., x, y);

样式

样式用于设置部件的外观,以优化显示界面和实现用户交互。
有两种设置方式,添加普通样式和添加本地样式,我个人理解为是创建一种可以给所有对象的和仅限给单一一个对象的区别,具体可参考代码,这两个程序现象都是一样的。

cpp 复制代码
void MyGui(void)
{
    static lv_style_t style; 					
    lv_style_init(&style); 								
    lv_style_set_bg_color(&style, lv_color_hex(0xf4b183)); 			

    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 150, 150);
    lv_obj_set_pos(obj, 50, 50);
    lv_obj_add_style(obj, &style, LV_STATE_DEFAULT);

    lv_obj_t * obj2 = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj2, 50, 50);
    lv_obj_align_to(obj2, obj, LV_ALIGN_CENTER, 100, 0);
    lv_obj_add_style(obj2, &style, LV_STATE_DEFAULT);
}
cpp 复制代码
void MyGui(void)
{
    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 150, 150);
    lv_obj_set_pos(obj, 50, 50);
    lv_obj_set_style_bg_color(obj, lv_color_hex(0xf4b183), LV_STATE_DEFAULT);

    lv_obj_t * obj2 = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj2, 50, 50);
    lv_obj_align_to(obj2, obj, LV_ALIGN_CENTER, 100, 0);
    lv_obj_set_style_bg_color(obj2, lv_color_hex(0xf4b183), LV_STATE_DEFAULT);
}

什么时候样式会生效:

bash 复制代码
enum {

    LV_STATE_DEFAULT     	=  0x0000,		 /* 默认状态 */
    LV_STATE_CHECKED     	=  0x0001,		 /* 切换或选中状态 */
    LV_STATE_FOCUSED     	=  0x0002,		 /* 通过键盘、编码器聚焦或通过触摸板、鼠标单击 */
    LV_STATE_FOCUS_KEY   	=  0x0004,		 /* 通过键盘、编码器聚焦 */
    LV_STATE_EDITED      	=  0x0008,		 /* 由编码器编辑 */
    LV_STATE_HOVERED     	=  0x0010,		 /* 鼠标悬停(现在不支持)*/
    LV_STATE_PRESSED     	=  0x0020,		 /* 已按下 */
    LV_STATE_SCROLLED    	=  0x0040,		 /* 滚动状态 */
    LV_STATE_DISABLED    	=  0x0080,		 /* 禁用状态 */
    ...
};

有哪些样式属性可以设置?

很多很多,包括但不限于,背景轮廓边框阴影等,这里举个例子,现象可见上方右图

cpp 复制代码
void MyGui(void)
{
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_bg_color(&style, lv_color_hex(0x2196F3)); // 背景
    lv_style_set_bg_opa(&style, LV_OPA_80);
    lv_style_set_border_width(&style, 3);// 边框
    lv_style_set_border_color(&style, lv_color_hex(0xFFFFFF));
    lv_style_set_radius(&style, 15); // 圆角
    lv_style_set_shadow_width(&style, 15);// 阴影
    lv_style_set_shadow_ofs_x(&style, 5);
    lv_style_set_shadow_ofs_y(&style, 5);

    lv_obj_t * obj = lv_obj_create(lv_scr_act());
    lv_obj_set_size(obj, 150, 150);
    lv_obj_set_pos(obj, 50, 50);
    lv_obj_add_style(obj, &style, LV_STATE_DEFAULT);
}

如何单独设置部件中某个部分的样式?

在LVGL中,单独设置部件(Widget)某个部分(Part)的样式,使用 lv_obj_set_style_xxx 函数的**第3个参数(selector)**来指定部件部分。

cpp 复制代码
enum {

    LV_PART_MAIN		= 0x000000, 	 	/* 主体,像矩形一样的背景 */
    LV_PART_SCROLLBAR	= 0x010000, 	 	/* 滚动条 */
    LV_PART_INDICATOR	= 0x020000, 	 	/* 指示器,指示当前值 */
    LV_PART_KNOB		= 0x030000,	 	/* 手柄或旋钮,用于调整参数值 */
    LV_PART_SELECTED	= 0x040000, 	 	/* 选项框,指示当前选择的选项 */
    LV_PART_ITEMS		= 0x050000, 	 	/* 相似的元素,例如单元格 */
    LV_PART_TICKS		= 0x060000, 	 	/* 刻度 */
    LV_PART_CURSOR		= 0x070000, 	 	/* 光标 */
};

例如:

cpp 复制代码
void MyGui(void)
{
    lv_obj_t * slider = lv_slider_create(lv_scr_act());
    lv_obj_set_size(slider, 200, 20);
    lv_obj_center(slider);
    lv_slider_set_value(slider, 50, LV_ANIM_OFF);

    lv_obj_set_style_bg_color(slider, lv_color_hex(0xE0E0E0), LV_PART_MAIN);
    lv_obj_set_style_radius(slider, 10, LV_PART_MAIN);
    lv_obj_set_style_bg_color(slider, lv_color_hex(0x2196F3), LV_PART_INDICATOR);
    lv_obj_set_style_radius(slider, 10, LV_PART_INDICATOR);
    lv_obj_set_style_bg_color(slider, lv_color_hex(0xFF5722), LV_PART_KNOB);
    lv_obj_set_style_radius(slider, 15, LV_PART_KNOB); 
    lv_obj_set_style_size(slider, 30, LV_PART_KNOB);    
}

API函数:上方示例中出现了一些,此处略


事件

LVGL中,当发生用户感兴趣的事情时,可以触发回调事件,以执行相关的操作,类似于单片机的中断回调函数。这里的回调函数可以检测事件类型以及触发对象。

cpp 复制代码
lv_obj_t * btn;
static void my_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);    // 获取事件类型
    lv_obj_t * obj = lv_event_get_target(e);         // 获取触发对象
    if(code == LV_EVENT_CLICKED && obj == btn) LV_LOG_USER("Clicked!");
}
void MyGui(void)
{
    btn = lv_btn_create(lv_scr_act());
    lv_obj_center(btn);
    lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL);
}

关于触发,可以有很多种,这里展示一部分(AI搜索)

事件 触发时机
LV_EVENT_PRESSED 按下(首次接触)
LV_EVENT_PRESSING 正在按(持续触发)
LV_EVENT_PRESS_LOST 按下后移出对象释放
LV_EVENT_SHORT_CLICKED 短按(按下+释放,无长按)
LV_EVENT_LONG_PRESSED 长按达到阈值
LV_EVENT_LONG_PRESSED_REPEAT 长按持续重复触发
LV_EVENT_CLICKED 点击(按下+释放,无移出)
LV_EVENT_RELEASED 释放
LV_EVENT_SCROLL_BEGIN 滚动开始
LV_EVENT_SCROLL_END 滚动结束
LV_EVENT_SCROLL 滚动中
LV_EVENT_GESTURE 手势识别(边缘滑动)
LV_EVENT_KEY 按键输入
LV_EVENT_FOCUSED 获得焦点
LV_EVENT_DEFOCUSED 失去焦点
LV_EVENT_LEAVE 离开对象(鼠标/触摸移出)

API函数

cpp 复制代码
添加事件:lv_obj_add_event_cb(obj, event_cb, event_code, user_data);
删除事件:lv_obj_remove_event_cb(obj,  event_cb);

部件使用

标签部件(lv_label

在 LVGL 中,标签部件主要用于文本显示,例如标题、提示信息等。

组成部分:主体(LV_PART_MAIN),滚动条(LV_PART_SCROLLBAR),选中的文本(LV_PART_SELECTED)

设置文本有三种形式

① 直接设置文本,存储文本的内存动态分配 :lv_label_set_text( label, "hallo \n lvgl");

② 文本不存储在动态内存,而是在指定的缓冲区中(慎用):lv_label_set_text_static( label,"hallo" );

③ 格式化显示文本,类似printf :lv_label_set_text_fmt( label, "Value: %d", 50 ) ;

同样的可以对文本单独设置:

① 背景颜色:lv_obj_set_style_bg_color( label, lv_color_hex(0xffe1d4), LV_STATE_DEFAULT );

② 字体大小:lv_obj_set_style_text_font( label, &lv_font_montserrat_30, LV_STATE_DEFAULT );

③ 文本颜色:lv_obj_set_style_text_color(label, lv_color_hex(0xf7b37b), LV_STATE_DEFAULT );

示例:这里可以设置个别文本的字体颜色

cpp 复制代码
void MyGui(void)
{
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello World!");
    lv_obj_center(label);
}
cpp 复制代码
void MyGui(void)
{
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_recolor( label, true ); 
    lv_label_set_text(label, "Hello #ff0000 World!#");
    lv_obj_center(label);
}

Q当文本长度超过部件大小的时候怎么显示?

默认情况下,如果没有限定标签部件大小,那它的大小自动扩展为文本大小。
也可以选择长文本模式

cpp 复制代码
enum {
    LV_LABEL_LONG_WRAP,             		/* 默认模式, 如果部件大小已固定,超出的文本将被剪切 */
    LV_LABEL_LONG_DOT,              		/* 将 label 右下角的最后 3 个字符替换为点... */
    LV_LABEL_LONG_SCROLL,           		/* 来回滚动 */
    LV_LABEL_LONG_SCROLL_CIRCULAR,  	/* 循环滚动 */
    LV_LABEL_LONG_CLIP,             		/* 直接剪切掉部件外面的文本部分 */
};
cpp 复制代码
void MyGui(void)
{
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "This is a long piece of text that requires scrolling to be read completely. LVGL provides various long text handling modes.");
    lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);  // 循环滚动
    // 必须设置固定宽度,否则不会滚动
    lv_obj_set_width(label, 200);  
    lv_obj_set_height(label, 30); 
    lv_obj_center(label);
    lv_obj_set_style_text_color(label, lv_color_hex(0xffe1d400), 0);
    lv_obj_set_style_text_font(label, &lv_font_montserrat_24, 0);
    lv_obj_set_style_bg_color(label, lv_color_hex(0x333333), 0); 
    lv_obj_set_style_bg_opa(label, LV_OPA_COVER, 0);
}

示例效果:正在滚动


按钮部件(lv_btn

在LVGL中,按钮部件与基础对象相比,没有新增任何功能。
按钮部件组成部分:主体(LV_PART_MAIN)

例程可参考上方事件,一个基础对象可以按下,一个按钮也能按下,倒也区别不大。

cpp 复制代码
static void my_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);    // 获取事件类型
    
    if(code == LV_EVENT_CLICKED) LV_LOG_USER("Clicked!");
    else if(code == LV_EVENT_VALUE_CHANGED) LV_LOG_USER("Value changed"); 
}
void MyGui(void)
{
    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_center(btn);
    
    lv_obj_t * label = lv_label_create(btn);
    lv_label_set_text(label, "Click Me");
    lv_obj_center(label);
    lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL);
}

部件拓展

中文字库

首先要准备需要使用的字体文件(ttf、otf等格式)

登录在线字体转换网站:https://lvgl.io/tools/fontconverter

添加字库文件到LVGL工程中,声明字体,编写测试代码

这里使用鸿蒙字体进行测试:

下载之后改名为.h文件,并添加到CMAKE目录中

另外8.3版本似乎不支持生成文件中的.static_bitmap = 0,,注释掉就好

cpp 复制代码
#include "lvgl/lvgl.h"
#include "mygui.h"
#include "src/temp_16.h"

LV_FONT_DECLARE(temp_16)       
void MyGui(void)
{
    lv_obj_t * label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "戏舟的嵌入式开源笔记");
    lv_obj_center(label);
    lv_obj_set_style_text_font(label, &temp_16, 0);
}
相关推荐
九成宫2 小时前
IT项目管理期末复习——Chapter 7 项目成本管理
笔记·项目管理·软件工程
小陈phd5 小时前
多模态大模型学习笔记(三十六)—— 扩散模型与可控生成:从AI涂鸦到精准作画的技术革命
人工智能·笔记·学习
雾岛听蓝6 小时前
Qt Widget控件属性详解
开发语言·经验分享·笔记·qt
m0_614619066 小时前
花了一下午学 Git,整理了一份学习笔记
笔记·git·学习
悠哉悠哉愿意8 小时前
【物联网学习笔记】TIM
笔记·单片机·嵌入式硬件·物联网·学习
中屹指纹浏览器8 小时前
2026指纹浏览器技术选型与落地踩坑指南:从需求匹配到风险规避
经验分享·笔记
编程百晓生8 小时前
《SAP FICO系统配置从入门到精通共40篇》021、CO-PC实战笔记:在制品与差异计算,那些年车间里的“账实不符”
服务器·人工智能·笔记·ar·从入门到精通·sap fico·sap fico系统
WYiQIU8 小时前
宇树科技Web前端岗(AI方向),这不算泄题吧......
前端·vue.js·人工智能·笔记·科技·面试·职场和发展
驭渊的小故事9 小时前
Java的类和对象的笔记
笔记