LVGL样式

LVGL样式概述

创建样式

在 LVGL 中,样式都是以对象的方式存在,一个对象可以描述一种样式。每个控件都可以独立添加样式,创建的样式之间互不影响。

可以使用 lv_style_t 类型创建一个样式并初始化:

static lv_style_t style;
lv_style_init(&style);

样式是延迟渲染的,因此需要使用 static 存储类别说明符或将其声明为全局变量。

样式是多方面的,不仅包括颜色和形状,还包括边距、边框,甚至动画变换效果等细节。

LVGL 中的样式从 CSS 中吸取了很多灵感,因此对样式的操作都类似 CSS

接下来,可以对得到的样式对象设置一些样式规则:

/* ... create and init style ... */
lv_style_set_radius(&style_btn_safe, 15);
lv_style_set_bg_opa(&style_btn_safe, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn_safe, lv_palette_main(LV_PALETTE_GREEN));
lv_style_set_border_width(&style_btn_safe, 5);

所有的设置样式函数都是 lv_style_set_...() 形式,完整的样式规则将在之后介绍。未指定的样式规则将保持控件的默认样式。

然后就可以将样式分配给控件,例如,以下创建了一个按钮并利用 lv_obj_add_style() 函数设置其样式为刚才创建的样式了:

lv_obj_t* btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_add_style(btn, &style_btn_safe, 0);

这样按钮的外观就会被改变了,效果为:

以上修改了按钮的颜色,如果对颜色的创建过程不太理解也不要紧,以后会介绍颜色的代码描述。可以简单地将 GREEN 改成其它颜色名来改变不同的颜色。设置样式的函数最后有一个参数 0 ,它代表的是样式的选择器,将会在接下来介绍。

一个文件内可以创建多种不同的样式对象,这样同一个界面中按钮可以表现出多种不同的样式。

样式的级联

所谓“级联”(cascading),指的是将多个样式分配给一个对象。此时如果多个样式间设置的样式属性有重复,那么将使用最后设置的样式值。也就是说,后设置的样式具有更高的优先级。

控件在创建时可以视为同时添加了一个默认的样式,因此在代码中指定的任意样式都会覆盖默认的样式。

还有一种特殊的局部样式(local styles),局部样式具有最高的优先级,但只对单个控件有效。局部样式的创建类似如下:

lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_RED), 0);

它们都是 lv_obj_set_style_...() 形式的函数。

局部样式一旦被设置,只能再次通过局部样式修改回来。因此,局部样式需要谨慎使用。

选择器

LVGL 的选择器(selector)与 CSS 不同。在 CSS 中,样式通过选择器选择需要作用的元素;而 LVGL 中,样式通过选择器作用于控件的部分。

要明白什么是控件的部分,需要分析控件的组成。例如,以下代码可以创建一个滑块(slider)控件:

lv_obj_t* slider01 = lv_slider_create(lv_scr_act());

滑块是一种调整类型的控件,用户可以通过拖动它的把手(knob)来调节滑块当前的数值。滑块默认的表现形式为:

仔细观察滑块的组成,滑块可以由主体外形、把手(knob)和进度指示条(indicator)组成。可以通过选择器单独设置这三个构成部分的样式。例如,假设需要更改这三个部分的样式,就可以通过选择器分别指定修改的结构:

static lv_style_t style_slider_main;
lv_style_init(&style_slider_main);
lv_style_set_bg_opa(&style_slider_main, LV_OPA_COVER);
lv_style_set_bg_color(&style_slider_main, lv_palette_main(LV_PALETTE_YELLOW));
/* using selectors */
lv_obj_add_style(slider01, &style_slider_main, LV_PART_MAIN);
lv_obj_set_style_radius(slider01, 0, LV_PART_KNOB);
lv_obj_set_style_bg_color(slider01, lv_palette_main(LV_PALETTE_RED), LV_PART_INDICATOR);

这里分别使用全局样式和局部样式修改控件的各个部分。修改之后,把手部分变成了方形,主体和进度进度的颜色都发生了变化:

选择器的一个更妙的用途是和控件状态做按位或运算,从而可以修改某个部分在某个状态下的样式。例如,选择器

lv_obj_add_style(slider01, &style_slider_main,
LV_PART_MAIN | LV_STATE_PRESSED);

使滑块的主体只有在按下时才会使用该样式(颜色被改变):

LVGL 的选择器在表现形式上效果非常像 CSS 的伪元素和伪类选择器。

滑块在拖动过程中,会不断触发 LV_EVENT_VALUE_CHANGED 事件,可以使用函数

static inline int32_t lv_slider_get_value(const lv_obj_t* obj);

获取当前获取的滑块数值(介于 0~100 )。更多的滑块 API 可以参考官方文档的介绍。

接下来详细地介绍样式可以设置的一些属性。

样式属性

尺寸和位置

要理解尺寸和位置是如何起作用的,首先要理解 LVGL 的盒子模型。官方文档给出了一张图,可以很好地描述一个控件的框架结构:

在设置尺寸的时候,长和宽指的是包括边框(border)厚度的长宽,也就是不包括轮廓(outline)的总长宽。

在设置位置的时候,设置的坐标指的是 border 左上角相对父容器的 Content area 的坐标,也就是说如果设置坐标为 0 的话,轮廓(outline)可能会被父容器的边框(border)遮盖。

下表总结了尺寸与位置有关的可用属性有:

属性 描述 默认值
width 宽度 由控件类别决定
min_width 最小宽度 0
max_width 最大宽度 屏幕的宽度
height 高度 由控件类别决定
min_height 最小宽度 0
max_height 最大宽度 屏幕的高度
align 对齐方式 左上方
x 对齐后在水平方向的偏移量 0
y 对齐后在竖直方向的偏移量 0

注意这里有一个最小或最大的宽度和高度,在上一节介绍 flex 和 grid 布局时就展示过控件宽度随布局自动调整的情况,因此可以给它们提供一个阈值防止过大或过小。

不过上一节还有一个地方没有提到:在设置宽度和高度时,除了使用确定的数值外,还可以使用百分比值 lv_pct(x) 来设置控件相对父容器的 Content area 的大小或位置。例如,样式

lv_style_set_width(&style, lv_pct(25));
lv_style_set_x(&style, lv_pct(50));

可以让一个控件的水平尺寸占据父容器的 1/2~3/4 的位置:

对于父容器而言,还可以使用 LV_SIZE_CONTENT 特殊单位调整其尺寸至可以容纳所有包含控件的合适值。例如,按钮就是一个这样的容器,它的默认样式就通过该值使得其宽度和高度可以自动适应包含的标签尺寸。

边框和边距

上图展示的文本框就有一个深灰色的边框。边框就无需额外描述了,与边框有关的样式属性有:

属性 描述 默认值
border_width 边框宽度,只能用绝对宽度描述 0
border_side 绘制哪些部分的边框 LV_SIDE_ALL
border_post 绘制顺序,设置 true 表示包含的子控件绘制完成了再绘制边框 false
... 与颜色有关的属性将在之后介绍

边框和主体部分之间被边距(padding)隔开。和边距有关的样式属性有:

属性 描述 默认值
pad_top 上边距 0
pad_bottom 下边距 0
pad_left 左边距 0
pad_right 右边距 0
pad_row 当控件拥有布局时,每行间的间距 0
pad_column 当控件拥有布局时,每列间的间距 0

不过在设置布局时,还提供了几个简写属性:可以使用 ...pad_all() 一并设置上下左右的边距;或使用 ...pad_hor()...pad_ver() 设置水平和垂直的边距;还可以使用 ...pad_gap() 设置行和列的间距。

轮廓

轮廓(outline)类似边框,但轮廓并不算在一个控件的主体内,因此设置坐标、尺寸等属性时都不包含轮廓的尺寸。

轮廓可设置的属性远比边框少。下表列出了轮廓的一些属性:

属性 描述 默认值
outline_width 轮廓宽度 0
outline_pad 轮廓到主体的间距 0
... 与颜色有关的属性将在之后介绍

轮廓和边框最根本的差异是两者不是同一个东西,因此可以在同一个元素同时使用不同样式的轮廓的边框来实现一些有趣的效果,例如:

lv_style_set_radius(&style, 0);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_border_width(&style, 5);
lv_style_set_border_opa(&style, LV_OPA_COVER);
lv_style_set_border_side(&style, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT);
lv_style_set_outline_width(&style, 4);
lv_style_set_outline_pad(&style, 1);
lv_style_set_outline_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

表现效果为:

阴影

阴影可以使控件看起来有立体感。下表列出了设置阴影的一些属性:

属性 描述 默认值
shadow_width 设置阴影的模糊半径 0
shadow_ofs_x 设置阴影的水平偏移量 0
shadow_ofs_y 设置阴影的垂直偏移量 0
shadow_spread 设置阴影的放大量 0
... 与颜色有关的属性将在之后介绍

例如,以下设置模糊半径为 50 的蓝色阴影:

lv_style_set_shadow_width(&style, 50);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_BLUE));

效果为:

以下设置放大有偏移的红色阴影:

lv_style_shadow_color(&style, lv_palette_main(LV_PALETTE_RED))
lv_style_set_shadow_width(&style, 15)
lv_style_set_shadow_ofs_x(&style, 10)
lv_style_set_shadow_ofs_y(&style, 20)
lv_style_set_shadow_spread(&style, 10)

效果为:

LVGL 中无法给同一个控件设置多个阴影叠加,从而实现更复杂的效果,这是比较可惜的一点。

文本样式

在创建控件时经常要使用文字,下表列出了能影响文字效果的一些属性:

属性 描述 默认值
text_font 设置文字的字体 默认字体
text_letter_space 字符间隔 0
text_line_space 设置多行文本的行间距 0
text_decor 设置文本装饰(下划线或删除线) LV_TEXT_DECOR_NONE
text_align 设置文本对齐方式 LV_TEXT_ALIGN_AUTO
... 与颜色有关的属性将在之后介绍

需要注意的是,文本的样式是可继承的,意思是如果子控件没有特别指定的话,它会使用父容器设置的文本样式。

在一段文本内可能存在许多种样式,对此,可以使用类似 CSS 的 span 来拆分样式在文本内的作用域。为了创建 span ,首先需要创建一个 span-group :

lv_obj_t* spangroup = lv_spangroup_create(lv_scr_act());
lv_obj_set_size(spangroup, 160, LV_SIZE_CONTENT);

创建的 span-group 和一般的控件没什么区别,可以给它添加一些样式:

lv_obj_set_style_border_color(spangroup, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_obj_set_style_border_width(spangroup, 1, 0);
lv_obj_set_style_pad_all(spangroup, 5, 0);

span-group 提供的以下函数使得它相比标签更适合用来处理大段的文本:

函数 介绍
lv_spangroup_set_align(obj, align) 设置文本的对齐
lv_spangroup_set_overflow(obj, overflow) 控制溢出文本的处理方式
lv_spangroup_set_indent(obj, indent) 设置文本的首行缩进,单位为像素
lv_spangroup_set_mode(obj, mode) 设置对多行文本的折行处理,可以参见枚举 lv_span_mode_t

有了 span-group 以后,可以使用以下代码从中创建一个 span 并设置文本:

lv_span_t* span = lv_spangroup_new_span(spangroup);
lv_span_set_text(span, "LVGL is an open-source graphics library");

每一个 span 都提供了一个独立的样式接口,可以单独设置范围内文本的样式:

lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_BLUE));

一个 span-group 可以创建多个 span ,并且它们的样式效果互不影响:

span = lv_spangroup_new_span(spangroup);
lv_span_set_text(span, "providing everything");
lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_UNDERLINE);
lv_style_set_text_font(&span->style, &lv_font_montserrat_20);
/* ... */
span = lv_spangroup_new_span(spangroup);
lv_span_set_text(span, "to create embedded GUI");

效果为:

可以注意到默认的 span-group 是没什么样式的。span-group 还有很多的 API ,具体可以参照官方文档的相关介绍。

其它样式

下表列出了一些其它的样式属性:

属性 描述 默认值
radius 设置控件的圆角,该属性会一并影响边框和轮廓 0,即无圆角
clip_corner 如果有圆角,是否要将 Content-aera 超出圆角的部分去除
layout 设置控件的布局方式 0
base_dir 设置文字的书写方向,它会同时影响布局的方向 默认书写方向
... 与颜色有关的属性将在之后介绍

在设置半径时可以使用百分数,例如 lv_pct(50) 将使控件变成圆形。

以上列出了大部分的样式属性,但是除了颜色外还有许多样式没有介绍,例如变换、动画、渐变等,这些留到之后介绍。LVGL 中还存在一些特殊的样式,它们是为相应的控件设计的,接下来介绍这些控件及样式。

基本图形:直线和弧线

直线

LVGL 中的直线(line)实际上指的是折线,因为它可以一次性连续绘制多条相接的线段。为了绘制折线,首先要准备一些端点的坐标:

static lv_point_t line_points[] = { {217, 36}, {35, 49}, {281, 163}, {110, 162}, {257, 111} };

然后可以通过这些端点来创建折线:

lv_obj_t* line1 = lv_line_create(lv_scr_act());
lv_line_set_points(line1, line_points, 5);

效果为:

创建的折线作为一个整体,实际上也是一个控件,当然可以给它加上各种属性:

static lv_style_t style_line;
lv_style_init(&style_line);
lv_style_set_align(&style_line, LV_ALIGN_TOP_MID);
lv_style_set_border_width(&style_line, 4);
lv_obj_add_style(line01, &style_line, 0);

效果为:

折线拥有一些特殊的样式属性,是其它控件所没有的。下表列出了折线的特殊属性:

属性 描述 默认值
line_width 设置线段宽度 0
line_dash_width 设置虚线实部分的距离 0
line_dash_gap 设置虚线虚部分的距离 0
line_rounded 设置线段端点是否为圆角
line_color 设置线段颜色 黑色
line_opa 设置颜色透明度 不透明

注意,虚线只对水平和垂直的线段有效,并且只有两个属性都不为 0 才有虚线的效果。

例如,样式:

lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_line_width(&style_line, 8);

表现效果为:

如果再添加上:

lv_style_set_line_dash_width(&style_line, 10)
lv_style_set_line_dash_gap(&style_line, 5)
lv_style_set_line_rounded(&style_line, true);

那么效果就变成:

关于折线还有一个函数 lv_line_set_y_invert(lv_obj_t *obj, bool en) 可以用来控制绘制的折线垂直翻转(即翻转 y 轴)。除此之外折线并没有什么可以介绍的。在后续还会介绍图表,可以绘制更美观的折线效果。

圆弧

LVGL 中的圆弧(arc)尽管和直线同属于基础控件,但圆弧的功能远比直线丰富,甚至 API 比起滑块这些复杂的控件都多。

首先简单创建一个圆弧,查看它的默认效果:

lv_obj_t* arc01 = lv_arc_create(lv_scr_act());

默认的效果为:

可以看出圆弧的在默认情况下,它的表现形式实际上就是弧形的滑块。如果想要得到纯粹的圆弧,可以将圆弧的把手删除:

lv_obj_remove_style(arc01, NULL, LV_PART_KNOB);
lv_obj_clear_flag(arc01, LV_OBJ_FLAG_CLICKABLE);

这里做了两件事:首先是将把手的样式删除,这里第二个参数 NULL 表示删去全部样式;其次将圆弧的可点击标志位清除,使它不再能接收用户的点击事件。这样圆弧看起来就纯粹多了:

还可以进一步删去圆弧的指示条(indicator),让它更像传统的圆弧。

默认的圆弧是开口向下的 270° 圆弧。为了设置圆弧的形状,可以使用函数

void lv_arc_set_angles(lv_obj_t *obj, uint16_t start, uint16_t end);
void lv_arc_set_bg_angles(lv_obj_t *obj, uint16_t start, uint16_t end);

分别修改前景和背景的圆弧起止范围,单位为角度。注意,圆弧的角度 0° 是正右方向,90° 是正下方向,以此类推。这两个函数都有单独设置起或止位置的版本。例如,设置

lv_arc_set_bg_angles(arc01, 0, 270);
lv_arc_set_end_angle(arc01, 180);

可以将圆弧的角度调整为:

圆弧也像直线一样具有特殊的样式,下表列出了圆弧具有的样式属性:

属性 描述 默认值
arc_width 设置圆弧宽度 0
arc_rounded 设置圆弧端点是否为圆角
arc_color 设置圆弧颜色 黑色
arc_opa 设置圆弧透明度 不透明
arc_img_src 设置圆弧填充图片 无填充图片

以上是官方文档的介绍,但这个默认值显然与实际不符。之所以会这样,原因是在 lv_conf.h 大约 514 行,启用过默认的样式:

/*A simple, impressive and very complete theme*/
#define LV_USE_THEME_DEFAULT 1

而该样式在初始化时,就会修改包括圆弧在内的一些样式,因此圆弧、按钮等控件才默认表现为这个模样。

圆弧可以作为一个基准让控件对齐。例如,可以使用

lv_arc_rotate_obj_to_angle(arc01, label, 25);

让一个标签旋转对齐圆弧的把手,第三个参数为半径的偏移量,效果为:

与其说是对齐把手,更准确的说法是对齐圆弧当前的值。例如,可以通过以下函数改变圆弧的值:

lv_arc_set_value(arc01, 20);

这样效果就很明显了:

圆弧默认的取值范围是 0~100 ,也可以通过 lv_arc_set_range(obj, min, max) 函数修改这一取值范围。除此之外,还有另一个函数 lv_arc_align_obj_to_angle(obj, obj_to_align, r_offset) 只对齐控件而不发生旋转。另外需要注意,应该先对齐圆弧后,再设置标签的对齐,否则标签会因为不是包含关系而不随之更新位置。

总体来说,圆弧因为不是纯粹的圆弧,因此它具有滑块的各种特征,例如可以响应 LV_EVENT_VALUE_CHANGED 事件,可以使用 lv_arc_get_value(obj) 获取值等。

首发于:http://frozencandles.fun/archives/361

参考资料/延伸阅读

https://docs.lvgl.io/master/overview/style.html

官方文档——样式简介

https://docs.lvgl.io/master/overview/style-props.html

官方文档——所有的样式属性简介

LVGL库入门教程04-样式的更多相关文章

  1. LVGL库入门教程01-移植到STM32(触摸屏)

    LVGL库移植STM32 LVGL库简介 LVGL(Light and Versatile Graphics Library)是一个免费.开源的嵌入式图形库,可以创建丰富.美观的界面,具有许多可以自定 ...

  2. LVGL库入门教程02-基本控件与交互

    LVGL 本质上是一个 GUI 库,它包含大量的控件(widget),即按钮.标签.滑块.菜单栏这种具有一定人机交互特征的组合图形.LVGL 在设计时,采用了一定面向对象编程的设计思路,有效降低了代码 ...

  3. LVGL库入门教程03-布局方式

    LVGL布局方式 LVGL的布局 上一节介绍了如何在 LVGL 中创建控件.如果在创建控件时不给控件安排布局,那么控件默认会被放在父容器的左上角. 可以使用 lv_obj_set_pos(obj, x ...

  4. LVGL库入门教程 - 颜色和图像

    颜色 构造颜色 在 LVGL 中,颜色以结构 lv_color_t 表示.在最开始移植整个工程时,曾经在 lv_conf.h 中修改过颜色深度: /*Color depth: 1 (1 byte pe ...

  5. LVGL库入门教程 - 动画

    动画可以说是 LVGL 中的特色之一,不过在使用动画前,请确保单片机具有足够的性能来维持足够的帧率. transition:过渡动画 当一个控件的状态发生改变时,可以让样式也发生变化以提醒用户.通过过 ...

  6. ExtJS入门教程04,这是一个超级好用的grid

    今天进行extjs入门教程的第四篇:grid. 来一份grid尝尝 小伙伴们都知道extjs的grid功能强大,更清楚功能强大的东西用起来必然会复杂.今天我们就从最简单的grid开始讲解. 先来一个最 ...

  7. 大爽Python入门教程 0-4 安装Pycharm

    大爽Python入门公开课教案 点击查看教程总目录 安装重量级IDE--Pycharm 一 下载 下面步骤1,2中网络卡顿的朋友, 请直接前往步骤3来下载. 使用搜索引擎搜索Pycharm, 打开搜索 ...

  8. 轻量级C语言实现的minixml解析库入门教程

    svn上的minixml源码下载.  svn co http://svn.msweet.org/mxml/tags/release-2.7/ 按照下载回来的源代码进行编译和安装.本教程只针对新手做一个 ...

  9. (转)轻量级C语言实现的minixml解析库入门教程

    svn上的minixml源码下载:svn co http://svn.msweet.org/mxml/tags/release-2.7/ 按照下载回来的源代码进行编译和安装.本教程只针对新手做一个引导 ...

随机推荐

  1. [源码解析] TensorFlow 分布式环境(8) --- 通信机制

    [源码解析] TensorFlow 分布式环境(8) --- 通信机制 目录 [源码解析] TensorFlow 分布式环境(8) --- 通信机制 1. 机制 1.1 消息标识符 1.1.1 定义 ...

  2. DFS与N皇后问题

    DFS与N皇后问题 DFS 什么是DFS DFS是指深度优先遍历也叫深度优先搜索. 它是一种用来遍历或搜索树和图数据结构的算法 注:关于树的一些知识可以去看<树的概念及基本术语>这篇文章 ...

  3. 新手入门C语言第九章:C函数

    C 函数 函数是一组一起执行一个任务的语句.每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数.您可以把代码划分到不同的函数中.如何划分代码到不同的函数中 ...

  4. js常用框架原理

    (function(){         //存储已经创建的模块     var moduleMap = {};     //判断是否已经加载过     var fileMap   = {};     ...

  5. 多行,溢出隐藏 css

     .ellipsis-line{width:200px; line-height:18px;font-size:14px; overflow:hidden; text-overflow:ellipsi ...

  6. Spring 源码(4)在Spring配置文件中自定义标签如何实现?

    Spring 配置文件自定义标签的前置条件 在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory的创建过 ...

  7. 分布式任务调度平台XXL-JOB安装及使用

    一.为什么需要任务调度平台 在Java中,传统的定时任务实现方案,比如Timer,Quartz等都或多或少存在一些问题: 不支持集群.不支持统计.没有管理平台.没有失败报警.没有监控等等而且在现在分布 ...

  8. Oracle 数据库表删除重复数据

    删除重复数据并保留一条 方法一 1.建立临时表,记录重复的数据 create table 临时表 as select a.字段1,a.字段2,max(a.rowid) as dataid from 原 ...

  9. Java 语言实现简易版扫码登录

    基本介绍 相信大家对二维码都不陌生,生活中到处充斥着扫码登录的场景,如登录网页版微信.支付宝等.最近学习了一下扫码登录的原理,感觉蛮有趣的,于是自己实现了一个简易版扫码登录的 Demo,以此记录一下学 ...

  10. Linux-centos8实现私有CA和证书申请

    创建CA相关目录,centos8不存在这些目录,需手动建立 [root@centos8-liyj ~]#mkdir -pv /etc/pki/CA/{certs,cr1,newcerts,privat ...