【全csdn最前沿LVGL9】Style样式

举报
人才程序员 发表于 2024/02/28 21:05:36 2024/02/28
【摘要】 @TOC 前言在LVGL 9的图形用户界面(GUI)开发中,Style(样式)是一项至关重要的功能,为界面元素的外观和行为提供了强大的定制能力。Style可以定义元素的颜色、边框、字体等各种视觉和行为属性,使得开发者能够轻松实现各种独特而漂亮的界面设计。本文将深入探讨LVGL 9中Style样式的使用方法,帮助开发者更好地掌握这一关键技术,创造出令人印象深刻的用户界面。 一、Style的介绍...

@TOC


前言

在LVGL 9的图形用户界面(GUI)开发中,Style(样式)是一项至关重要的功能,为界面元素的外观和行为提供了强大的定制能力。Style可以定义元素的颜色、边框、字体等各种视觉和行为属性,使得开发者能够轻松实现各种独特而漂亮的界面设计。本文将深入探讨LVGL 9中Style样式的使用方法,帮助开发者更好地掌握这一关键技术,创造出令人印象深刻的用户界面。


一、Style的介绍

样式是一个 lv_style_t 类型的变量,可以包含诸如边框宽度、文本颜色等属性。这类似于CSS中的类(class)的概念。
样式可以被分配给对象以改变它们的外观。在分配时,可以指定目标部分(CSS中的伪元素)和目标状态(伪类)。例如,可以在滑块的旋钮处添加 style_blue,以在按下状态下改变其外观。
同一样式可以被任意数量的对象共享使用。
样式可以被层叠,这意味着可以分配多个样式给一个对象,每个样式可以有不同的属性。因此,并非所有属性都必须在一个样式中指定。LVGL将在样式中搜索属性,直到找到定义该属性的样式,或者如果没有样式定义,则使用默认值。例如,style_btn 可以产生默认的灰色按钮,而 style_btn_red 可以仅添加一个 background-color=red 以覆盖背景颜色。
最近添加的样式具有更高的优先级。这意味着如果一个属性在两个样式中都有指定,对象中最新添加的样式将被使用。
一些属性(例如文本颜色)可以从父级继承,如果在对象中没有指定。
对象还可以具有比“普通”样式更高优先级的本地样式。
与CSS不同(在其中伪类描述不同的状态,例如 :focus),在LVGL中,属性被分配给给定的状态。
可以在对象状态改变时应用过渡效果。

二、State状态

我们的lvgl的样式是在不同状态下有不同的样式的
这些对象可以处于以下状态的组合:

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)禁用状态
LV_STATE_USER_1:(0x1000)自定义状态
LV_STATE_USER_2:(0x2000)自定义状态
LV_STATE_USER_3:(0x4000)自定义状态
LV_STATE_USER_4:(0x8000)自定义状态

一个对象可以同时处于多种状态的组合,比如同时处于焦点和被按下的状态,表示为 LV_STATE_FOCUSED | LV_STATE_PRESSED。

可以向任何状态或状态组合添加样式。例如,为默认和按下的状态设置不同的背景颜色。如果一个属性在某个状态中没有定义,将使用最匹配状态的属性。通常,这意味着将使用具有 LV_STATE_DEFAULT 的属性。如果甚至对于默认状态也没有设置属性,将使用默认值(见后文)。

但是,“最匹配状态的属性”到底是什么意思呢?状态的优先级由它们的值表示(见上面的列表)。值越高,优先级越高。为了确定要使用哪个状态的属性,让我们来看一个例子。想象一下,背景颜色定义如下:

LV_STATE_DEFAULT:白色
LV_STATE_PRESSED:灰色
LV_STATE_FOCUSED:红色

最初,对象处于默认状态,因此这是一个简单的情况:属性在对象当前状态中完美定义为白色。

当对象被按下时,有两个相关的属性:默认为白色(默认与每个状态相关),按下为灰色。按下的状态具有0x0020的优先级,这比默认状态的0x0000优先级更高,因此将使用灰色。

当对象被聚焦时,与按下状态相同的情况发生,红色将被使用。(聚焦状态优先级高于默认状态)

当对象既聚焦又被按下时,灰色和红色都可以工作,但按下状态的优先级高于聚焦状态,因此将使用灰色。

可以设置例如玫瑰色为 LV_STATE_PRESSED | LV_STATE_FOCUSED。在这种情况下,此组合状态的优先级为0x0020 + 0x0002 = 0x0022,高于按下状态的优先级,因此将使用玫瑰色。

当对象处于已选状态时,没有设置此状态的背景颜色的属性。因此,由于没有更好的选择,对象将保持来自默认状态属性的白色。

一些建议:
状态的优先级(值)相当直观,用户会自然而然地期望的。例如,如果对象聚焦,用户仍然希望看到它是否被按下,因此按下状态具有更高的优先级。如果聚焦状态具有更高的优先级,它将覆盖按下的颜色。
如果要为所有状态设置属性(例如,红色背景颜色),只需为默认状态设置即可。如果对象找不到其当前状态的属性,它将回退到默认状态的属性。
使用按位或运算的状态描述复杂的情况。(例如,按下 + 已选 + 聚焦)
对于不同的状态,使用不同的样式元素可能是一个不错的主意。例如,为释放、按下、已选 + 按下、聚焦等状态找到不同的背景颜色。

三、级联样式

在样式层叠中,不需要在一个样式中设置所有属性。可以向对象添加更多样式,使后添加的样式修改或扩展外观。例如,创建一个通用的灰色按钮样式,然后创建一个新的红色按钮样式,只设置新的背景颜色。

这与CSS中使用类列表的方式很相似,例如 <div class=".btn .btn-red">。

后添加的样式优先于先前设置的样式。因此,在上述灰色/红色按钮示例中,应该先添加普通按钮样式,然后再添加红色样式。然而,仍然要考虑状态的优先级。因此,让我们来看下面的情况:

基本按钮样式为默认状态定义深灰色,为按下状态定义浅灰色。

红色按钮样式仅在默认状态下将背景颜色定义为红色。

在这种情况下,当按钮释放时(处于默认状态),它将是红色,因为在最近添加的样式(红色)中找到了完全匹配。当按钮被按下时,浅灰色是更好的匹配,因为它完美地描述了当前状态,所以按钮将是浅灰色。

四、Style的继承

有些属性(通常是与文本相关的属性)可以从父对象的样式中继承。仅当对象的样式中未设置给定属性时(即使在默认状态下也未设置),才会应用继承。在这种情况下,如果属性是可继承的,将在父级中查找该属性的值,直到某个对象为该属性指定一个值为止。父级将使用自己的状态来确定该值。因此,如果按钮被按下,文本颜色是从这里继承的,那么将使用按下状态的文本颜色。

五、组件

对象可以由各个部分组成,每个部分都可以拥有自己的样式。

在LVGL中存在以下预定义的部分:

LV_PART_MAIN: 像矩形一样的背景
LV_PART_SCROLLBAR: 滚动条
LV_PART_INDICATOR: 指示器,例如用于滑块、进度条、开关或复选框的勾选框
LV_PART_KNOB: 类似于手柄,用于调整数值
LV_PART_SELECTED: 指示当前选定的选项或部分
LV_PART_ITEMS: 如果小部件具有多个相似的元素(例如表格单元),则使用此部分
LV_PART_CURSOR: 标记特定的位置,例如文本区域或图表的光标
LV_PART_CUSTOM_FIRST: 从这里开始可以添加自定义部分标识符。

上面这些可以搭配我们的State表示某一个状态下我的某个地方是什么样式

六、设置样式属性

样式被存储在lv_style_t类型的变量中。样式变量应该是静态的、全局的,或者是动态分配的。换句话说,它们不能是在函数退出时销毁的局部变量。在使用样式之前,应该使用lv_style_init(&my_style)来初始化样式。初始化样式后,可以添加或更改属性。
使用lv_style_set_<property_name>(&style, <value>);来设置样式

示例代码:

static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588));//背景颜色
lv_style_set_bg_opa(&style_btn, LV_OPA_50);//背景的透明度
lv_style_set_border_width(&style_btn, 2);//边框的宽度
lv_style_set_border_color(&style_btn, lv_color_black());//边框的颜色

static lv_style_t style_btn_red;
lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_plaette_main(LV_PALETTE_RED));
lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER);

移除样式的某个属性:lv_style_remove_prop(&style, LV_STYLE_<property_name>);
例如:lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);移除背景颜色

获取样式某个属性的值:lv_style_get_prop(&style, LV_STYLE_<property_name>, &v);
v的类型为lv_style_value_t
返回值为是否获取成功

示例代码:

lv_style_value_t v;
lv_res_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
if(res == LV_RES_OK) {  /*Found*/
    do_something(v.color);
}

我们可以通过lv_res_t里面的成员获取值

七、添加和移除样式

7.1 添加

我们可以使用这个函数lv_obj_add_style(obj, &style, <selector>)<selector> 是由部分和状态进行逻辑 OR 操作得到的值,用于指定应该添加样式的对象。

例如:

LV_PART_MAIN | LV_STATE_DEFAULT
LV_STATE_PRESSED: 处于按下状态的主要部分。LV_PART_MAIN 可以省略。
LV_PART_SCROLLBAR: 默认状态下的滚动条部分。LV_STATE_DEFAULT 可以省略。
LV_PART_SCROLLBAR | LV_STATE_SCROLLED: 当对象正在滚动时的滚动条部分。
LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED: 对象被按下且同时处于选中状态时的指示器部分。

使用这个函数添加样式示例代码:

lv_obj_add_style(btn, &style_btn, 0);                     /*默认*/
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED);        /*改变点击时的颜色*/

7.2 替换样式

我们可以使用这个函数lv_obj_replace_style(obj, old_style, new_style, selector)
注意:这个函数只有在选择器与lv_obj_add_style中使用的选择器匹配时,才会用new_style替换old_style。两种样式,即old_style和new_style,都不能为NULL(有专门的添加和移除函数来处理)。如果在对象的样式中多次存在old_style和选择器的组合,所有的出现都将被替换。函数的返回值指示是否至少发生了一次成功的替换。

示例代码:

lv_obj_add_style(btn, &style_btn, 0);                      /*Add a button style*/
lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0);  /*Replace the button style with a different one*/

7.3 移除样式

lv_obj_remove_style_all(obj)
lv_obj_remove_style(obj, style, selector)

我们可以使用上面这两个函数来移除样式
第一个移除所有,第二个根据参数2和参数3进行移除

这个函数只有在选择器与lv_obj_add_style()中使用的选择器匹配时,才会移除样式。style参数可以是NULL,以仅检查选择器并移除所有匹配的样式。选择器可以使用LV_STATE_ANY和LV_PART_ANY值,以从任何状态或部分移除样式。

7.4 当对象实时改变了样式去通知对象刷新样式

如果已分配给对象的样式发生更改(即添加或更改了属性),则使用该样式的对象应该收到通知。有三种选项可以实现这一点:
如果你知道更改的属性可以通过简单的重绘来应用(例如颜色或不透明度的更改),只需调用lv_obj_invalidate(obj)或lv_obj_invalidate(lv_screen_active())

如果更改或添加了更复杂的样式属性,并且你知道受到该样式影响的对象是哪些,调用lv_obj_refresh_style(obj, part, property)。要刷新所有部分和属性,请使用lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY)

要让 LVGL 检查所有对象,看看它们是否使用了某个样式,并在需要时刷新它们,请调用lv_obj_report_style_change(&style)。如果样式是 NULL,则会通知所有对象样式已更改。

八、获取一个对象的属性值

要获取属性的最终值,考虑到级联、继承、本地样式和过渡(见下文),可以使用如下的属性获取函数:lv_obj_get_style_<property_name>(obj, <part>)。这些函数使用对象当前的状态,如果没有更好的候选对象,则返回默认值。例如:

lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);

其中lv_color_t的定义如下:

typedef struct {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} lv_color_t;

九、本地样式

除了“普通”样式外,对象还可以存储本地样式。这个概念类似于CSS中的内联样式(例如 <div style=“color:red”>),但有一些修改。

本地样式与普通样式类似,但不能在其他对象之间共享。如果使用,本地样式会自动分配,并在对象被删除时释放。它们非常适用于对对象进行本地定制。

与CSS不同,LVGL的本地样式可以分配给状态(伪类)和部分(伪元素)。

要设置本地属性,可以使用类似lv_obj_set_style_<property_name>(obj, <value>, <selector>)的函数。例如:

lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);

那么本地样式和普通样式的区别是什么:
范围和共享性:
普通样式(Global Styles):普通样式可以被多个对象共享,它们可以在整个应用程序中重复使用。修改普通样式会影响到所有使用该样式的对象。
本地样式(Local Styles):本地样式是特定于对象的,不能被其他对象共享。每个对象都可以有自己的本地样式,它们只适用于拥有它们的对象。本地样式的修改只影响到拥有该样式的对象。

自动分配和释放:
普通样式:普通样式需要手动分配和释放。它们可以在应用程序的整个生命周期中保持不变,除非显式更改或释放。
本地样式:本地样式在对象创建时自动分配,并在对象删除时自动释放。这意味着在对象的生命周期内,不需要手动管理本地样式的分配和释放。

作用范围:
普通样式:普通样式通常用于全局设置,可以影响多个对象的外观和行为。
本地样式:本地样式通常用于对特定对象进行局部定制。它们允许对象在不影响其他对象的情况下拥有自己独特的外观和行为。

状态和部分:
普通样式:普通样式通常不与特定状态或部分相关联,它们适用于对象的所有状态和部分。
本地样式:本地样式可以与特定状态或部分相关联。这意味着可以为对象的不同状态或部分分配不同的本地样式,从而实现更细粒度的外观定制。

十、过度动画

默认情况下,当对象改变状态时(例如,被按下),新状态的属性会立即设置。然而,通过使用过渡效果,可以在状态变化时播放动画。例如,按下按钮时,其背景颜色可以在300毫秒内动画变为按下时的颜色。

过渡效果的参数存储在样式中。可以设置以下过渡效果的属性:

过渡时间
开始过渡之前的延迟时间
动画路径(也称为定时或缓动函数)
要进行动画的属性

过渡效果的属性可以针对每个状态进行定义。例如,在默认状态下设置500毫秒的过渡时间意味着当对象转到默认状态时将应用500毫秒的过渡时间。在按下状态下设置100毫秒的过渡时间将导致在转到按下状态时应用100毫秒的过渡时间。这个例子的配置会使得快速转到按下状态,然后缓慢返回到默认状态。

为了描述一个过渡效果,需要初始化一个lv_transition_dsc_t变量,并将其添加到一个样式中。

示例代码:

static const lv_style_prop_t trans_props[] = {
                                            LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
                                            0, /*End marker*/
};//要进行过渡效果的样式属性

static lv_style_transition_dsc_t trans1;
/*对过渡效果结构体进行初始化。参数包括过渡效果结构体(&trans1)、
要进行过渡的样式属性数组(trans_props)、动画路径(lv_anim_path_ease_out表示渐出的动画路径)、
过渡的持续时间(duration_ms,以毫秒为单位)、过渡开始前的延迟时间(delay_ms,以毫秒为单位)*/
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);

lv_style_set_transition(&style1, &trans1);

十一、主题

主题是一组样式的集合。如果有活动主题,LVGL将其应用于每个创建的小部件。这将为UI提供一个默认外观,然后可以通过添加更多样式进行修改。

每个显示屏可以具有不同的主题。例如,您可以在TFT上使用彩色主题,在辅助的单色显示器上使用单色主题。

要为显示屏设置主题,需要两个步骤:

初始化主题
将初始化的主题分配给显示屏。
主题初始化函数可以有不同的原型。此示例演示了如何设置“默认”主题。

lv_theme_t * th = lv_theme_default_init(display,  /*Use the DPI, size, etc from this display*/
                                        LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN,   /*Primary and secondary palette*/
                                        false,    /*Light or dark mode*/
                                        &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/

lv_display_set_theme(display, th); /*Assign the theme to the display*/

lv_theme_default_init 是用于初始化默认主题的函数,它有以下参数:
display:显示屏对象,函数会使用该显示屏的 DPI(每英寸点数)、尺寸等信息进行初始化。
LV_COLOR_PALETTE_BLUE:主题的主色调(primary palette)。这是一个蓝色调色板,用于主题的基本颜色。
LV_COLOR_PALETTE_CYAN:主题的次要色调(secondary palette)。这是一个青色调色板,用于主题的其他颜色。
false:用于指定主题的模式,false 表示深色模式(dark mode)。
&lv_font_montserrat_10:用于指定主题中小号字体的字体对象。在这个例子中,使用了 Montserrat 字体的 10 号字体。
&lv_font_montserrat_14:用于指定主题中正常号字体的字体对象。在这个例子中,使用了 Montserrat 字体的 14 号字体。
&lv_font_montserrat_18:用于指定主题中大号字体的字体对象。在这个例子中,使用了 Montserrat 字体的 18 号字体。
这些参数一起构成了一个主题对象,该对象包含了显示屏的信息、颜色调色板、主题模式以及不同字体大小的字体对象。然后,通过 lv_display_set_theme(display, th) 将这个主题分配给显示屏,以便应用在创建的小部件上。

如果你使用的模拟器,则display在这里:在这里插入图片描述
如果不是模拟器则是在这创建的:

lv_disp_t *display = lv_disp_drv_register(&disp_drv);

如果在 lv_conf.h 文件中启用了包含的主题选项,而且通过 LV_USE_THEME_DEFAULT 启用了默认主题,LVGL 将在创建显示屏时自动进行初始化并设置默认主题。


总结

通过本文的
介绍,我们深入了解了LVGL 9中Style样式的强大功能。从基本的颜色和边框设置开始,我们学习了如何使用Style为界面元素定制各种视觉效果。我们还讨论了如何在运行时动态修改样式,使得界面能够更灵活地适应不同的用户交互和应用状态。Style的灵活性为开发者提供了丰富的选择,使他们能够创建出独特而富有吸引力的用户界面。通过巧妙地运用LVGL 9的Style样式,开发者可以在GUI开发中取得更为出色的成果。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。