[LVGL] 部件lv_obj | 样式lv_style | LV_PART_ | LV_STATE_

示例代码:https://github.com/lvy010/Cpp-Lib-test/tree/main/LVGL

第三章:部件(lv_obj)

欢迎回来!在第二章:显示屏(lv_display)中,我们学习了如何配置LVGL以理解物理屏幕并进行绘制。

我们为图形创作搭建了"画布"。但若没有画笔或特定形状,画布又有何用?

**部件(lv_obj)**正是为此而生!部件是LVGL用户界面的基本构建单元,可类比为乐高积木。我们并非直接在屏幕上绘制原始像素,而是使用预定义的形状元素,如按钮、文本标签、滑块或简单矩形区域。每个"积木"都有其特定用途、外观和行为。

本章目标是通过构建包含文本的简单按钮界面,理解这些基础构建单元。

什么是部件?(lv_obj_t基类)

本质上,LVGL屏幕上的所有元素(按钮、标签、滑块、图像等)都基于lv_obj_t类型。我们可以将lv_obj_t视为"基础部件",所有其他专用部件均由其派生而来。

所有部件均继承lv_obj_t的通用能力:

  • 位置:在屏幕上的坐标
  • 尺寸:长宽像素值
  • 父子关系:在容器内的组织结构
  • 样式:视觉呈现(颜色、边框、字体等,详见后续章节)
  • 事件:对用户输入的响应(如点击)

创建任意LVGL部件时,均会获得lv_obj_t类型的指针,该指针即部件的控制句柄。

创建首个部件:屏幕、按钮与标签

我们从构建带文本的简单按钮开始。

1. 屏幕:顶级部件

添加界面元素前,需先创建"屏幕"。在LVGL中,屏幕是特殊的无父部件lv_obj_t,自动占据整个显示屏(lv_display)空间。

通过向lv_obj_create()传入NULL父指针创建屏幕:

c 复制代码
#include "lvgl.h" // 必须包含LVGL主头文件

void create_my_ui() 
{
    // 创建屏幕对象。由于是首个无父对象(NULL),自动成为活动屏幕
    lv_obj_t * screen_main = lv_obj_create(NULL);
    // 屏幕自动覆盖全屏,无需设置尺寸/位置

    // 显式加载此屏幕(若为首个屏幕通常自动加载)
    lv_screen_load(screen_main);

    // 现在可向屏幕添加其他部件!
}
  • lv_obj_create(NULL):创建基础矩形部件,因父指针为NULL转为屏幕
  • lv_obj_t * screen_main:保存新屏幕的句柄,用于添加子部件
  • lv_screen_load(screen_main):确保当前显示该屏幕(首屏可省略)

2. 向屏幕添加按钮

按钮(lv_button)是继承自lv_obj_t的专用部件,默认支持点击交互。

创建部件时需指定parent参数,此处父级为screen_main

c 复制代码
// ...(接续前段代码)

void create_my_ui() 
{
    lv_obj_t * screen_main = lv_obj_create(NULL);
    lv_screen_load(screen_main);

    // 在'screen_main'上创建按钮
    lv_obj_t * button1 = lv_button_create(screen_main);

    // 设置尺寸(宽度,高度,单位像素)
    lv_obj_set_size(button1, 120, 50);

    // 设置相对父级(screen_main)的坐标
    // 使用居中对齐函数简化定位
    lv_obj_set_align(button1, LV_ALIGN_CENTER);
}
  • lv_button_create(screen_main):创建按钮部件,父级为screen_main
  • lv_obj_set_size():通用尺寸设置函数,适用于所有部件
  • LV_ALIGN_CENTER:便捷居中函数,替代手动计算坐标

3. 为按钮添加标签(文本)

标签(lv_label)是专用于文本显示的部件。将其父级设为按钮即可实现文本嵌入:

c 复制代码
// ...(接续前段代码)

void create_my_ui() 
{
    lv_obj_t * screen_main = lv_obj_create(NULL);
    lv_screen_load(screen_main);

    lv_obj_t * button1 = lv_button_create(screen_main);
    lv_obj_set_size(button1, 120, 50);
    lv_obj_set_align(button1, LV_ALIGN_CENTER);

    // 在'button1'上创建标签
    lv_obj_t * label1 = lv_label_create(button1);

    // 设置标签文本
    lv_label_set_text(label1, "Hello LVGL!");

    // 居中标签文本
    lv_obj_set_align(label1, LV_ALIGN_CENTER);
}
  • lv_label_create(button1):创建标签,父级为按钮
  • lv_label_set_text():标签专用文本设置函数
  • 再次使用LV_ALIGN_CENTER实现文本居中

完成上述配置后,结合第二章的显示设置并定期调用lv_timer_handler(),即可在屏幕中央看到带文本的按钮

理解父子关系

父子层级是LVGL的核心组织逻辑,类似于树状结构或嵌套容器:

  • 每个部件(除屏幕)有唯一父级
  • 父级可含多个子级
  • 相对定位:子级坐标基于父级左上角,父级移动时子级自动跟随
  • 可见性裁剪:子级超出父级区域部分默认不可见,类似视窗效果

这种层级结构简化了复杂界面的管理:

创建与删除部件

部件支持运行时动态创建/删除,这对内存受限的嵌入式系统尤为重要:

  • 创建 :使用lv_obj_create(parent)lv_<部件类型>_create(parent)
  • 删除lv_obj_delete(部件句柄)会递归删除所有子级并释放内存
c 复制代码
// 部件删除示例
void delete_my_button(lv_obj_t * button_to_delete) 
{
    // 删除按钮及其子级(标签)
    lv_obj_delete(button_to_delete);
}

// 延迟删除(毫秒)
lv_obj_delete_delayed(button1, 1000);

// 异步删除(下次定时器周期)
lv_obj_delete_async(button1);

部件底层

调用lv_obj_create()lv_<部件>_create()时,LVGL执行以下流程:

每个lv_obj_t结构体包含关键信息:

  • lv_area_t coords:部件坐标与尺寸(左上/右下坐标)
  • lv_obj_t * parent:父级指针
  • lv_obj_flag_t flags:状态标志位(如可点击性、隐藏状态)
  • lv_state_t state:可视化状态(如按下、禁用)
  • const lv_obj_class_t * class_p:部件类型指针(如基础对象类、按钮类)

调用lv_obj_add_flag()lv_obj_add_state()时,实质是修改flagsstate变量:

c 复制代码
// 简化版lv_obj_add_flag实现
void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f) {
    obj->flags |= f; // 位运算添加标志
    lv_obj_invalidate(obj); // 触发重绘
}

// 应用示例:启用按钮点击
lv_obj_add_flag(button1, LV_OBJ_FLAG_CLICKABLE);

LVGL核心文件lv_obj.c处理基础部件的创建、管理与绘制逻辑。专用部件(如按钮)在基础对象上扩展特性与默认样式。这种分层设计使开发者无需关注底层绘制细节,专注实现业务逻辑。

总结

我们已掌握LVGL部件的核心概念:

  • lv_obj_t是所有UI元素的基类
  • 部件通过父子层级组织,屏幕作为顶级容器
  • 支持动态创建/删除以优化内存使用
  • 通用属性通过lv_obj函数管理,专用功能由部件类型函数实现

理解部件机制是构建可视化界面的关键。下一步我们将学习如何美化部件外观!

下一章:样式(lv_style)


第四章:样式(lv_style)

第三章:部件(lv_obj)中,我们学习了如何将按钮和文本标签等基础元素放置到屏幕。

虽然实现了简单的"Hello LVGL!"按钮,但它的外观略显单调------灰色矩形搭配黑色文本。

**样式(lv_style)**正是为此而生!想象我们拥有素色乐高积木(部件),现在要为它们添加涂装、纹样、圆角甚至发光特效。

LVGL的样式系统正是这样的视觉装饰系统,通过预定义外观模板实现界面元素的美化。

什么是样式?(lv_style_t

LVGL样式本质是lv_style_t类型变量,可存储多种视觉属性集合,类似于网页设计的CSS类。其核心属性包括:

  • 色彩属性:背景色、边框色、文本色
  • 尺寸定位:内边距(部件内部留白)、外边距(外部留白)、边框宽度
  • 形态特征:圆角半径(实现圆角矩形)
  • 文本特性:字体、字间距、行距、对齐方式
  • 视觉效果:阴影、透明度、图像重着色

创建并应用首个样式

让我们优化第三章按钮的视觉效果,实现蓝色背景与圆角设计:

1. 初始化样式

首先声明静态或全局的lv_style_t变量并进行初始化:

c 复制代码
#include "lvgl.h"

// 声明静态样式变量
static lv_style_t my_button_style;

void setup_my_styles() {
    // 样式初始化(关键步骤)
    lv_style_init(&my_button_style);

    // 后续将添加属性配置
}
  • static lv_style_t my_button_style:静态声明确保样式变量持久化
  • lv_style_init():样式初始化标准操作,清空原有配置

2. 配置样式属性

设置蓝色背景与圆角属性:

c 复制代码
void setup_my_styles() {
    lv_style_init(&my_button_style);

    // 设置海蓝色背景
    lv_style_set_bg_color(&my_button_style, lv_color_hex(0x007BFF));
    lv_style_set_bg_opa(&my_button_style, LV_OPA_COVER); // 完全覆盖不透明

    // 设置10像素圆角半径
    lv_style_set_radius(&my_button_style, 10);
}
  • lv_color_hex():十六进制颜色码转LVGL颜色结构
  • LV_OPA_COVER:全不透明宏定义(LV_OPA_TRANSP为全透明)

3. 应用样式至部件

将样式绑定至第三章创建的按钮:

c 复制代码
void create_my_ui() {
    lv_obj_t * screen_main = lv_obj_create(NULL);
    lv_screen_load(screen_main);

    lv_obj_t * button1 = lv_button_create(screen_main);
    lv_obj_set_size(button1, 120, 50);
    lv_obj_set_align(button1, LV_ALIGN_CENTER);

    // 应用新样式(参数0表示LV_PART_MAIN|LV_STATE_DEFAULT)
    lv_obj_add_style(button1, &my_button_style, 0);

    lv_obj_t * label1 = lv_label_create(button1);
    lv_label_set_text(label1, "Hello LVGL!");
    lv_obj_set_align(label1, LV_ALIGN_CENTER);
}

部件组成单元(LV_PART_

复杂部件(如滑块)包含多个视觉组件,LVGL允许对各部分独立设置样式:

组成单元宏定义 描述
LV_PART_MAIN 主体背景区域
LV_PART_SCROLLBAR 滚动条组件
LV_PART_INDICATOR 进度指示器(如滑块进度条)
LV_PART_KNOB 可拖动旋钮
LV_PART_ITEMS 多元素部件(如表格单元格)

示例:为滑块旋钮设置独立样式:

c 复制代码
lv_obj_t * my_slider = lv_slider_create(screen_main);

static lv_style_t knob_style;
lv_style_init(&knob_style);
lv_style_set_bg_color(&knob_style, lv_color_hex(0xFF0000)); // 红色旋钮
lv_style_set_radius(&knob_style, LV_RADIUS_CIRCLE); // 正圆形

// 仅应用于旋钮部分
lv_obj_add_style(my_slider, &knob_style, LV_PART_KNOB);

状态响应(LV_STATE_

GUI元素在不同交互状态下呈现差异化的视觉效果:

状态宏定义 触发条件
LV_STATE_DEFAULT 默认未激活状态
LV_STATE_PRESSED 按压状态
LV_STATE_FOCUSED 焦点状态(键盘/触控选择)
LV_STATE_CHECKED 切换激活状态(如开关开启)
LV_STATE_DISABLED 禁用不可用状态
LV_STATE_HOVERED 鼠标悬停状态

示例:按钮按压状态变色:

c 复制代码
static lv_style_t my_button_pressed_style;

void setup_my_styles() {
    // 默认状态样式配置...

    // 按压状态深蓝色背景
    lv_style_init(&my_button_pressed_style);
    lv_style_set_bg_color(&my_button_pressed_style, lv_color_hex(0x0056b3));
}

void create_my_ui() {
    // 应用默认状态样式
    lv_obj_add_style(button1, &my_button_style, 0);
    
    // 应用按压状态样式
    lv_obj_add_style(button1, &my_button_pressed_style, LV_STATE_PRESSED);
}

层叠样式(优先级覆盖)

LVGL支持多重样式叠加,遵循后添加样式优先原则:

c 复制代码
// 基础按钮样式(灰色背景)
static lv_style_t base_button_style;
lv_style_set_bg_color(&base_button_style, lv_color_grey());
lv_style_set_radius(&base_button_style, 5);

// 变体样式(仅修改背景色)
static lv_style_t red_button_variant_style;
lv_style_set_bg_color(&red_button_variant_style, lv_color_red());

lv_obj_t * button = lv_button_create(screen);
lv_obj_add_style(button, &base_button_style, 0);       // 基础样式
lv_obj_add_style(button, &red_button_variant_style, 0);// 红色变体样式(覆盖背景色)

最终效果:红色背景+5px圆角,体现后添加样式的属性覆盖特性。

属性继承机制

部分文本相关属性具备继承性,子部件未定义时继承父级设置:

c 复制代码
// 全局字体样式(屏幕级继承)
static lv_style_t global_font_style;
lv_style_set_text_font(&global_font_style, &lv_font_montserrat_28);
lv_style_set_text_color(&global_font_style, lv_color_black());

lv_obj_t * screen_main = lv_obj_create(NULL);
lv_obj_add_style(screen_main, &global_font_style, 0); // 子部件继承字体设置

本地样式(快速定制)

支持直接为特定部件设置独立属性,优先级最高:

c 复制代码
lv_obj_t * button = lv_button_create(screen_main);
lv_obj_set_style_bg_color(button, lv_color_make(255, 128, 0), 0); // 橙色本地样式
lv_obj_set_style_radius(button, 20, 0); // 独立圆角设置

样式系统工作原理

LVGL采用延迟计算机制优化渲染性能:

内部数据结构说明:

c 复制代码
struct _lv_obj_t {
    lv_obj_style_t * styles; // 样式指针数组
    uint16_t style_cnt;       // 样式数量
    // 其他属性...
};

总结

本章全面掌握LVGL样式系统,核心要点包括:

  • 样式初始化与属性配置流程
  • 部件组成单元与交互状态的精确控制
  • 层叠样式优先级规则与继承机制
  • 本地样式的快速定制方法
  • 样式系统的底层存储与计算逻辑

通过灵活运用样式系统,开发者可实现专业级视觉设计效果,为后续布局系统学习奠定基础。

图形化可以使用:

下一章:布局系统(lv_flex, lv_grid)