[English]

Styles(风格样式)

显示原文

Styles are used to set the appearance of objects. Styles in lvgl are heavily inspired by CSS. The concept in a nutshell is as follows: - A style is an lv_style_t variable which can hold properties like border width, text color and so on. It's similar to a class in CSS.

  • Styles can be assigned to objects to change their appearance. Upon assignment, the target part (pseudo-element in CSS) and target state (pseudo class) can be specified. For example one can add style_blue to the knob of a slider when it's in pressed state.

  • The same style can be used by any number of objects.

  • Styles can be cascaded which means multiple styles may be assigned to an object and each style can have different properties. Therefore, not all properties have to be specified in a style. LVGL will search for a property until a style defines it or use a default if it's not specified by any of the styles. For example style_btn can result in a default gray button and style_btn_red can add only a background-color=red to overwrite the background color.

  • The most recently added style has higher precedence. This means if a property is specified in two styles the newest style in the object will be used.

  • Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in an object.

  • Objects can also have local styles with higher precedence than "normal" styles.

  • Unlike CSS (where pseudo-classes describe different states, e.g. :focus), in LVGL a property is assigned to a given state.

  • Transitions can be applied when the object changes state.


"Styles" 用于设置对象的外观。lvgl中的样式受到CSS的启发。简而言之,概念如下:

  • 样式是一个 lv_style_t 变量,它可以保存诸如边框宽度、文字颜色等属性。类似于CSS中的 class

  • 样式可以分配给对象以改变它们的外观。在分配时,可以指定目标部分(CSS中的伪元素)和目标状态(伪类)。例如,当滑块处于按下状态时,可以为其添加 style_blue

  • 同样的样式可以被任意数量的对象使用。

  • 样式可以被级联,这意味着可以将多个样式分配给一个对象,每个样式可以具有不同的属性。因此,并非所有的属性都必须在一个样式中指定。 LVGL将在样式中搜索属性,直到找到定义该属性的样式,或者如果没有任何样式指定,则使用默认值。 例如, style_btn 可以生成默认的灰色按钮,而 style_btn_red 可以仅添加 background-color=red 覆盖背景颜色。

  • 最近添加的样式具有更高的优先级。这意味着,如果一个属性在两个样式中都指定了,对象中最新的样式将被使用。

  • 一些属性(例如文字颜色)可以从父级继承,如果没有在对象中指定的话。

  • 对象也可以具有优先级比“正常”样式更高的本地样式。

  • 与CSS不同(在CSS中,伪类描述不同的状态,例如 :focus ),在LVGL中,属性分配给了给定的状态。

  • 当对象更改状态时,可以应用过渡效果。

States(状态)

显示原文

The objects can be in the combination of the following states:

An object can be in a combination of states such as being focused and pressed at the same time. This is represented as LV_STATE_FOCUSED | LV_STATE_PRESSED.

A style can be added to any state or state combination. For example, setting a different background color for the default and pressed states. If a property is not defined in a state the best matching state's property will be used. Typically this means the property with LV_STATE_DEFAULT is used.˛ If the property is not set even for the default state the default value will be used. (See later)

But what does the "best matching state's property" really mean? States have a precedence which is shown by their value (see in the above list). A higher value means higher precedence. To determine which state's property to use let's take an example. Imagine the background color is defined like this:

  1. Initially the object is in the default state, so it's a simple case: the property is perfectly defined in the object's current state as white.

  2. When the object is pressed there are 2 related properties: default with white (default is related to every state) and pressed with gray. The pressed state has 0x0020 precedence which is higher than the default state's 0x0000 precedence, so gray color will be used.

  3. When the object is focused the same thing happens as in pressed state and red color will be used. (Focused state has higher precedence than default state).

  4. When the object is focused and pressed both gray and red would work, but the pressed state has higher precedence than focused so gray color will be used.

  5. It's possible to set e.g. rose color for LV_STATE_PRESSED | LV_STATE_FOCUSED. In this case, this combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than the pressed state's precedence so rose color would be used.

  6. When the object is in the checked state there is no property to set the background color for this state. So for lack of a better option, the object remains white from the default state's property.

Some practical notes:

  • The precedence (value) of states is quite intuitive, and it's something the user would expect naturally. E.g. if an object is focused the user will still want to see if it's pressed, therefore the pressed state has a higher precedence. If the focused state had a higher precedence it would overwrite the pressed color.

  • If you want to set a property for all states (e.g. red background color) just set it for the default state. If the object can't find a property for its current state it will fall back to the default state's property.

  • Use ORed states to describe the properties for complex cases. (E.g. pressed + checked + focused)

  • It might be a good idea to use different style elements for different states. For example, finding background colors for released, pressed, checked + pressed, focused, focused + pressed, focused + pressed + checked, etc. states is quite difficult. Instead, for example, use the background color for pressed and checked states and indicate the focused state with a different border color.


对象可以处于以下状态的组合中:

对象可以处于多个状态的组合中,例如同时处于聚焦和按下状态。这被表示为 LV_STATE_FOCUSED | LV_STATE_PRESSED

样式可以应用于任何状态或状态组合。例如,为默认和按下状态设置不同的背景颜色。 如果某个状态中未定义属性,则会使用与之最匹配的状态的属性。通常情况下,这意味着将使用带有 LV_STATE_DEFAULT 的属性。 如果即使对于默认状态,某个属性也未设置,则将使用默认值。(稍后会详细说明)

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

  1. 初始情况下,对象处于默认状态,所以情况很简单:属性在对象当前状态中完全定义为白色。

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

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

  4. 当对象处于聚焦和按下状态时,灰色和红色都可以。但是按下状态的优先级高于聚焦状态,因此会使用灰色。

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

  6. 当对象处于选中状态时,没有属性来设置此状态的背景颜色。由于缺乏更好的选择,对象仍然保持默认状态的白色属性。

一些实用的注意事项:

  • 状态的优先级(值)非常直观,这是用户自然期望的结果。例如,如果对象处于聚焦状态,用户仍然希望看到它是否被按下,因此按下状态具有更高的优先级。如果聚焦状态具有更高的优先级,它将覆盖按下的颜色。

  • 如果要为所有状态(例如红色背景颜色)设置属性,只需为默认状态设置即可。如果对象在其当前状态下找不到属性,它将退回到默认状态的属性。

  • 使用 OR 运算符来描述复杂情况下的属性(例如,按下 + 选中 + 聚焦)。

  • 对于不同的状态,使用不同的样式元素可能是个好主意。例如,为释放、按下、选中 + 按下、聚焦、聚焦 + 按下、聚焦 + 按下 + 选中等状态找到背景颜色是相当困难的。相反,例如,使用按下和选中状态的背景颜色,并使用不同的边框颜色指示聚焦状态。

Cascading styles(层叠样式)

显示原文

It's not required to set all the properties in one style. It's possible to add more styles to an object and have the latter added style modify or extend appearance. For example, create a general gray button style and create a new one for red buttons where only the new background color is set.

This is much like in CSS when used classes are listed like <div class=".btn .btn-red">.

Styles added later have precedence over ones set earlier. So in the gray/red button example above, the normal button style should be added first and the red style second. However, the precedence of the states are still taken into account. So let's examine the following case:

  • the basic button style defines dark-gray color for the default state and light-gray color for the pressed state

  • the red button style defines the background color as red only in the default state

In this case, when the button is released (it's in default state) it will be red because a perfect match is found in the most recently added style (red). When the button is pressed the light-gray color is a better match because it describes the current state perfectly, so the button will be light-gray.


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

这就像在CSS中使用类列表一样,比如 <div class=".btn .btn-red">

后添加的样式具有优先权,优先于先前设置的样式。因此,在上述灰色/红色按钮的例子中,应该先添加普通按钮样式,然后再添加红色样式。但是,状态的优先级仍然会被考虑。因此,让我们来看一个例子:

  • 基本按钮样式为默认状态设置了暗灰色的颜色,为按下状态设置了浅灰色的颜色

  • 红色按钮样式只在默认状态下将背景颜色设置为红色

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

Inheritance(继承)

显示原文

Some properties (typically those related to text) can be inherited from the parent object's styles. Inheritance is applied only if the given property is not set in the object's styles (even in default state). In this case, if the property is inheritable, the property's value will be searched in the parents until an object specifies a value for the property. The parents will use their own state to determine the value. So if a button is pressed, and the text color comes from here, the pressed text color will be used.


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

Parts(部分)

显示原文

Objects can be composed of parts which may each have their own styles.

The following predefined parts exist in LVGL:

For example a Slider has three parts:

  • Background

  • Indicator

  • Knob

This means all three parts of the slider can have their own styles. See later how to add styles to objects and parts.


物体可以由具有自己样式的“部分”组成。

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

例如,一个 滑动条 具有三个部分:

  • 背景

  • 指示器

  • 把手

这意味着滑动条的这三个部分都可以有自己的样式。稍后我们将看到如何向对象和部分添加样式。

Initialize styles and set/get properties(初始化样式和设置/获取属性)

显示原文

Styles are stored in lv_style_t variables. Style variables should be static, global or dynamically allocated. In other words they cannot be local variables in functions which are destroyed when the function exits. Before using a style it should be initialized with lv_style_init(&my_style). After initializing a style, properties can be added or changed.

Property set functions looks like this: lv_style_set_<property_name>(&style, <value>); For example:

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);

To remove a property use:

lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);

To get a property's value from a style:

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

lv_style_value_t has 3 fields:

  • num: for integer, boolean and opacity properties

  • color: for color properties

  • ptr: for pointer properties

To reset a style (free all its data) use:

lv_style_reset(&style);

Styles can be built as const too to save RAM:

const lv_style_const_prop_t style1_props[] = {
   LV_STYLE_CONST_WIDTH(50),
   LV_STYLE_CONST_HEIGHT(50),
   LV_STYLE_CONST_PROPS_END
};

LV_STYLE_CONST_INIT(style1, style1_props);

Later const style can be used like any other style but (obviously) new properties can not be added.


样式存储在 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_BG_COLOR);

从样式中获取属性的值:

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

lv_style_value_t 包含 3 个字段:

  • num:用于整数、布尔值和不透明度属性

  • color:用于颜色属性

  • ptr:用于指针属性

要重置一个样式(释放其所有数据),可以使用:

lv_style_reset(&style);

样式也可以定义为 const,以节省 RAM:

const lv_style_const_prop_t style1_props[] = {
   LV_STYLE_CONST_WIDTH(50),
   LV_STYLE_CONST_HEIGHT(50),
   LV_STYLE_CONST_PROPS_END
};

LV_STYLE_CONST_INIT(style1, style1_props);

以后可以像其他样式一样使用 const 样式,但显然不能添加新属性。

Add and remove styles to a widget(向部件添加和删除样式)

显示原文

A style on its own is not that useful. It must be assigned to an object to take effect.


一个单独的样式并不是特别有用,必须将其分配给一个对象才能生效。

Add styles(添加样式)

显示原文

To add a style to an object use lv_obj_add_style(obj, &style, <selector>). <selector> is an OR-ed value of parts and state to which the style should be added. Some examples:

Using lv_obj_add_style():

lv_obj_add_style(btn, &style_btn, 0);                     /*Default button style*/
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED);        /*Overwrite only some colors to red when pressed*/

为了给对象添加样式,请使用 lv_obj_add_style(obj, &style, <selector>)<selector> 是一个按位或运算的值,用于指定要添加样式的部分和状态。下面是一些示例:

使用 lv_obj_add_style()

lv_obj_add_style(btn, &style_btn, 0);                     /*默认按钮样式*/
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED);        /*仅在按下时将部分颜色更改为红色*/

Replace styles(替换样式)

显示原文

To replace a specific style of an object use lv_obj_replace_style(obj, old_style, new_style, selector). This function will only replace old_style with new_style if the selector matches the selector used in lv_obj_add_style. Both styles, i.e. old_style and new_style, must not be NULL (for adding and removing separate functions exist). If the combination of old_style and selector exists multiple times in obj's styles, all occurrences will be replaced. The return value of the function indicates whether at least one successful replacement took place.

Using lv_obj_replace_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*/

使用下列语句来替换对象的特定样式:lv_obj_replace_style(obj, old_style, new_style, selector)。 此函数仅在 selector 匹配 lv_obj_add_style 中使用的 selector 时,才会将 old_style 替换为 new_style。 两种样式,即 old_stylenew_style 都不能为 NULL (添加和删除分别存在不同的函数)。 如果 obj 的样式中存在多个 old_styleselector 的组合,所有出现的情况都将被替换。函数的返回值指示是否至少进行了一次成功替换。

使用 lv_obj_replace_style()

lv_obj_add_style(btn, &style_btn, 0);                      /* 添加按钮样式 */
lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0);  /* 用不同的样式替换按钮样式 */

Remove styles(删除样式)

显示原文

To remove all styles from an object use lv_obj_remove_style_all(obj).

To remove specific styles use lv_obj_remove_style(obj, style, selector). This function will remove style only if the selector matches with the selector used in lv_obj_add_style(). style can be NULL to check only the selector and remove all matching styles. The selector can use the LV_STATE_ANY and LV_PART_ANY values to remove the style from any state or part.


从对象中删除所有样式,请使用 lv_obj_remove_style_all(obj)

要删除特定的样式,请使用 lv_obj_remove_style(obj, style, selector)。 此函数将仅在 selectorlv_obj_add_style() 中使用的 selector 匹配时删除 stylestyle 可以是 NULL,以仅检查 selector 并删除所有匹配的样式。 selector 可以使用 LV_STATE_ANYLV_PART_ANY 值,从任何状态或部件中删除样式。

Report style changes(通知样式更改)

显示原文

If a style which is already assigned to an object changes (i.e. a property is added or changed), the objects using that style should be notified. There are 3 options to do this:

  1. If you know that the changed properties can be applied by a simple redraw (e.g. color or opacity changes) just call lv_obj_invalidate(obj) or lv_obj_invalidate(lv_screen_active()).

  2. If more complex style properties were changed or added, and you know which object(s) are affected by that style call lv_obj_refresh_style(obj, part, property). To refresh all parts and properties use lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY).

  3. To make LVGL check all objects to see if they use a style and refresh them when needed, call lv_obj_report_style_change(&style). If style is NULL all objects will be notified about a style change.


如果已经分配给一个对象的样式发生改变(例如添加或更改属性),那么使用该样式的对象应该收到通知。有三种选项可以做到这一点:

  1. 如果你知道改变的属性可以通过简单的重绘来应用(例如颜色或不透明度的变化),只需调用: lv_obj_invalidate(obj) 或者 lv_obj_invalidate(lv_screen_active())

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

  3. 要让LVGL检查所有对象以查看它们是否使用了样式,并在需要时刷新它们,请调用: lv_obj_report_style_change(&style)。如果 styleNULL,则所有对象将收到有关样式更改的通知。

Get a property's value on an object(获取对象的属性值)

显示原文

To get a final value of property

  • considering cascading, inheritance, local styles and transitions (see below)

  • property get functions like this can be used: lv_obj_get_style_<property_name>(obj, <part>). These functions use the object's current state and if no better candidate exists they return a default value. For example:

lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);

获取物体的最终属性值

  • 考虑级联、继承、本地样式和过渡效果(见下文)

  • 可以使用此类属性获取函数: lv_obj_get_style_<property_name>(obj, <part>)。 这些函数使用物体的当前状态,如果没有更好的候选项,则返回默认值。 例如:

lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);

Local styles(本地样式)

显示原文

In addition to "normal" styles, objects can also store local styles. This concept is similar to inline styles in CSS (e.g. <div style="color:red">) with some modification.

Local styles are like normal styles, but they can't be shared among other objects. If used, local styles are allocated automatically, and freed when the object is deleted. They are useful to add local customization to an object.

Unlike in CSS, LVGL local styles can be assigned to states (pseudo-classes) and parts (pseudo-elements).

To set a local property use functions like lv_obj_set_style_<property_name>(obj, <value>, <selector>); For example:

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

除了“普通”样式外,对象还可以存储本地样式。 这个概念类似于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);

Properties(属性)

显示原文

For the full list of style properties click here.


点击这里获取完整的样式属性列表 here

Typical background properties(典型的背景属性)

显示原文

In the documentation of the widgets you will see sentences like "The widget uses the typical background properties". These "typical background properties" are the ones related to:

  • Background

  • Border

  • Outline

  • Shadow

  • Padding

  • Width and height transformation

  • X and Y translation


在小部件的文档中,您会看到类似于“该小部件使用典型的背景属性”的句子。这些“典型的背景属性”包括:

  • 背景

  • 边框

  • 轮廓

  • 阴影

  • 填充

  • 宽度和高度转换

  • X 和 Y 的平移

Transitions(过渡特效)

显示原文

By default, when an object changes state (e.g. it's pressed) the new properties from the new state are set immediately. However, with transitions it's possible to play an animation on state change. For example, on pressing a button its background color can be animated to the pressed color over 300 ms.

The parameters of the transitions are stored in the styles. It's possible to set

  • the time of the transition

  • the delay before starting the transition

  • the animation path (also known as the timing or easing function)

  • the properties to animate

The transition properties can be defined for each state. For example, setting a 500 ms transition time in the default state means that when the object goes to the default state a 500 ms transition time is applied. Setting a 100 ms transition time in the pressed state causes a 100 ms transition when going to the pressed state. This example configuration results in going to the pressed state quickly and then going back to default slowly.

To describe a transition an lv_transition_dsc_t variable needs to be initialized and added to a style:

/*Only its pointer is saved so must static, global or dynamically allocated */
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;
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);

lv_style_set_transition(&style1, &trans1);

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

过渡的参数存储在样式中。可以设置:

  • 过渡时间

  • 开始过渡前的延迟

  • 动画路径(也称为定时或缓动函数)

  • 要进行动画处理的属性

过渡属性可以为每个状态定义。例如,在默认状态下设置500毫秒的过渡时间意味着当对象进入默认状态时,将应用500毫秒的过渡时间。 在按下状态下设置100毫秒的过渡时间,则在进入按下状态时会有100毫秒的过渡。这个例子的配置意味着迅速进入按下状态,然后慢慢返回默认状态。

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

/*Only its pointer is saved so must static, global or dynamically allocated */
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;
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);

lv_style_set_transition(&style1, &trans1);

Opacity, Blend modes and Transformations(不透明度,混合模式和变换)

显示原文

If the opa, blend_mode, transform_angle, or transform_zoom properties are set to their non-default value LVGL creates a snapshot about the widget and all its children in order to blend the whole widget with the set opacity, blend mode and transformation properties.

These properties have this effect only on the MAIN part of the widget.

The created snapshot is called "intermediate layer" or simply "layer". If only opa and/or blend_mode is set to a non-default value LVGL can build the layer from smaller chunks. The size of these chunks can be configured by the following properties in lv_conf.h:

  • LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate this size of memory.

  • LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if LV_LAYER_SIMPLE_BUF_SIZE couldn't be allocated.

If transformation properties were also used the layer can not be rendered in chunks, but one larger memory needs to be allocated. The required memory depends on the angle, zoom and pivot parameters, and the size of the area to redraw, but it's never larger than the size of the widget (including the extra draw size used for shadow, outline, etc).

If the widget can fully cover the area to redraw, LVGL creates an RGB layer (which is faster to render and uses less memory). If the opposite case ARGB rendering needs to be used. A widget might not cover its area if it has radius, bg_opa != 255, has shadow, outline, etc.

The click area of the widget is also transformed accordingly.


如果 opablend_modetransform_angletransform_zoom 属性被设置为它们的非默认值,LVGL将为小部件及其所有子级创建一个快照,以便将整个小部件与设置的不透明度、混合模式和变换属性混合在一起。

这些属性仅在小部件的 MAIN 部分上产生作用。

创建的快照称为“中间层”或简称为“层”。如果只有 opa 和/或 blend_mode 设置为非默认值,LVGL可以从较小的块构建图层。这些块的大小可以通过 lv_conf.h 中的以下属性配置:

  • LV_LAYER_SIMPLE_BUF_SIZE: [字节] 的理想目标缓冲区大小。LVGL将尝试分配此内存大小。

  • LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [字节] 如果无法分配 LV_LAYER_SIMPLE_BUF_SIZE,则使用该大小。

如果还使用了变换属性,则图层无法以块的形式渲染,而需要分配更大的内存。所需的内存取决于角度、缩放和枢轴参数以及要重绘的区域大小,但永远不会大于小部件的大小(包括用于阴影、轮廓等的额外绘制大小)。

如果小部件可以完全覆盖要重绘的区域,LVGL将创建一个RGB图层(渲染速度更快,占用的内存更少)。如果相反情况需要使用ARGB渲染。如果小部件具有半径、 bg_opa != 255、有阴影、轮廓等,则小部件可能无法覆盖其区域。

小部件的点击区域也会相应地进行变换。

Color filter(色彩过滤)

TODO

Themes(主题)

显示原文

Themes are a collection of styles. If there is an active theme LVGL applies it on every created widget. This will give a default appearance to the UI which can then be modified by adding further styles.

Every display can have a different theme. For example, you could have a colorful theme on a TFT and monochrome theme on a secondary monochrome display.

To set a theme for a display, two steps are required:

  1. Initialize a theme

  2. Assign the initialized theme to a display.

Theme initialization functions can have different prototypes. This example shows how to set the "default" theme:

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*/

The included themes are enabled in lv_conf.h. If the default theme is enabled by LV_USE_THEME_DEFAULT LVGL automatically initializes and sets it when a display is created.


主题是一系列的样式。如果有一个激活的主题,LVGL会应用它到每个创建的部件上。这将为UI提供一个默认的外观,可以通过添加更多样式来进行修改。

每个显示器可以有不同的主题。例如,你可以在TFT上有一个多彩的主题,在第二个单色显示器上有一个单色的主题。

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

  1. 初始化一个主题

  2. 将初始化的主题分配给一个显示器。

主题初始化函数可以有不同的原型。下面的例子显示了如何设置“默认”主题:

lv_theme_t * th = lv_theme_default_init(display,  /*使用该显示器的DPI、大小等*/
                                        LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN,   /*主色和辅助色板*/
                                        false,    /*亮或暗模式*/
                                        &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*小、正常、大字体*/

lv_display_set_theme(display, th); /*将主题分配给显示器*/

包含的主题在 lv_conf.h 中已启用。如果默认主题由 LV_USE_THEME_DEFAULT 启用,LVGL在创建显示器时会自动初始化和设置它。

Extending themes(扩展主题)

显示原文

Built-in themes can be extended. If a custom theme is created, a parent theme can be selected. The parent theme's styles will be added before the custom theme's styles. Any number of themes can be chained this way. E.g. default theme -> custom theme -> dark theme.

lv_theme_set_parent(new_theme, base_theme) extends the base_theme with the new_theme.

There is an example for it below.


内置主题可以进行扩展。如果创建了自定义主题,则可以选择父主题。父主题的样式将在自定义主题的样式之前添加。可以通过这种方式链接任意数量的主题。例如,默认主题 -> 自定义主题 -> 深色主题。

lv_theme_set_parent(new_theme, base_theme)new_theme 扩展了 base_theme

下面有一个示例。

Examples

[English]

Size styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Using the Size, Position and Padding style properties
 */
void lv_example_style_1(void)
{
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_radius(&style, 5);

    /*Make a gradient*/
    lv_style_set_width(&style, 150);
    lv_style_set_height(&style, LV_SIZE_CONTENT);

    lv_style_set_pad_ver(&style, 20);
    lv_style_set_pad_left(&style, 5);

    lv_style_set_x(&style, lv_pct(50));
    lv_style_set_y(&style, 80);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);

    lv_obj_t * label = lv_label_create(obj);
    lv_label_set_text(label, "Hello");
}

#endif

Background styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
 * Using the background style properties
 */
void lv_example_style_2(void)
{
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_radius(&style, 5);

    /*Make a gradient*/
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    static lv_grad_dsc_t grad;
    grad.dir = LV_GRAD_DIR_VER;
    grad.stops_count = 2;
    grad.stops[0].color = lv_palette_lighten(LV_PALETTE_GREY, 1);
    grad.stops[0].opa = LV_OPA_COVER;
    grad.stops[1].color = lv_palette_main(LV_PALETTE_BLUE);
    grad.stops[1].opa = LV_OPA_COVER;

    /*Shift the gradient to the bottom*/
    grad.stops[0].frac  = 128;
    grad.stops[1].frac  = 192;

    lv_style_set_bg_grad(&style, &grad);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_obj_center(obj);
}

#endif

Border styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
 * Using the border style properties
 */
void lv_example_style_3(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    /*Set a background color and a radius*/
    lv_style_set_radius(&style, 10);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

    /*Add border to the bottom+right*/
    lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_border_width(&style, 5);
    lv_style_set_border_opa(&style, LV_OPA_50);
    lv_style_set_border_side(&style, LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_RIGHT);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_obj_center(obj);
}

#endif

Outline styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
 * Using the outline style properties
 */
void lv_example_style_4(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    /*Set a background color and a radius*/
    lv_style_set_radius(&style, 5);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

    /*Add outline*/
    lv_style_set_outline_width(&style, 2);
    lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_outline_pad(&style, 8);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_obj_center(obj);
}

#endif

Shadow styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES

/**
 * Using the Shadow style properties
 */
void lv_example_style_5(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    /*Set a background color and a radius*/
    lv_style_set_radius(&style, 5);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1));

    /*Add a shadow*/
    lv_style_set_shadow_width(&style, 55);
    lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_BLUE));

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_obj_center(obj);
}

#endif

Image styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Using the Image style properties
 */
void lv_example_style_6(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    /*Set a background color and a radius*/
    lv_style_set_radius(&style, 5);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 3));
    lv_style_set_border_width(&style, 2);
    lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));

    lv_style_set_image_recolor(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_image_recolor_opa(&style, LV_OPA_50);
    lv_style_set_transform_rotation(&style, 300);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_image_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);

    LV_IMAGE_DECLARE(img_cogwheel_argb);
    lv_image_set_src(obj, &img_cogwheel_argb);

    lv_obj_center(obj);
}

#endif

Arc styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_ARC

/**
 * Using the Arc style properties
 */
void lv_example_style_7(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    lv_style_set_arc_color(&style, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_arc_width(&style, 4);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_arc_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_obj_center(obj);
}
#endif

Text styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LABEL

/**
 * Using the text style properties
 */
void lv_example_style_8(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    lv_style_set_radius(&style, 5);
    lv_style_set_bg_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 2));
    lv_style_set_border_width(&style, 2);
    lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_pad_all(&style, 10);

    lv_style_set_text_color(&style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_text_letter_space(&style, 5);
    lv_style_set_text_line_space(&style, 20);
    lv_style_set_text_decor(&style, LV_TEXT_DECOR_UNDERLINE);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_label_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);
    lv_label_set_text(obj, "Text of\n"
                      "a label");

    lv_obj_center(obj);
}

#endif

Line styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LINE

/**
 * Using the line style properties
 */
void lv_example_style_9(void)
{
    static lv_style_t style;
    lv_style_init(&style);

    lv_style_set_line_color(&style, lv_palette_main(LV_PALETTE_GREY));
    lv_style_set_line_width(&style, 6);
    lv_style_set_line_rounded(&style, true);

    /*Create an object with the new style*/
    lv_obj_t * obj = lv_line_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);

    static lv_point_precise_t p[] = {{10, 30}, {30, 50}, {100, 0}};
    lv_line_set_points(obj, p, 3);

    lv_obj_center(obj);
}

#endif

Transition

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Creating a transition
 */
void lv_example_style_10(void)
{
    static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR, LV_STYLE_BORDER_WIDTH, 0};

    /* A default transition
     * Make it fast (100ms) and start with some delay (200 ms)*/
    static lv_style_transition_dsc_t trans_def;
    lv_style_transition_dsc_init(&trans_def, props, lv_anim_path_linear, 100, 200, NULL);

    /* A special transition when going to pressed state
     * Make it slow (500 ms) but start  without delay*/
    static lv_style_transition_dsc_t trans_pr;
    lv_style_transition_dsc_init(&trans_pr, props, lv_anim_path_linear, 500, 0, NULL);

    static lv_style_t style_def;
    lv_style_init(&style_def);
    lv_style_set_transition(&style_def, &trans_def);

    static lv_style_t style_pr;
    lv_style_init(&style_pr);
    lv_style_set_bg_color(&style_pr, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_border_width(&style_pr, 6);
    lv_style_set_border_color(&style_pr, lv_palette_darken(LV_PALETTE_RED, 3));
    lv_style_set_transition(&style_pr, &trans_pr);

    /*Create an object with the new style_pr*/
    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style_def, 0);
    lv_obj_add_style(obj, &style_pr, LV_STATE_PRESSED);

    lv_obj_center(obj);
}

#endif

Using multiple styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Using multiple styles
 */
void lv_example_style_11(void)
{
    /*A base style*/
    static lv_style_t style_base;
    lv_style_init(&style_base);
    lv_style_set_bg_color(&style_base, lv_palette_main(LV_PALETTE_LIGHT_BLUE));
    lv_style_set_border_color(&style_base, lv_palette_darken(LV_PALETTE_LIGHT_BLUE, 3));
    lv_style_set_border_width(&style_base, 2);
    lv_style_set_radius(&style_base, 10);
    lv_style_set_shadow_width(&style_base, 10);
    lv_style_set_shadow_offset_y(&style_base, 5);
    lv_style_set_shadow_opa(&style_base, LV_OPA_50);
    lv_style_set_text_color(&style_base, lv_color_white());
    lv_style_set_width(&style_base, 100);
    lv_style_set_height(&style_base, LV_SIZE_CONTENT);

    /*Set only the properties that should be different*/
    static lv_style_t style_warning;
    lv_style_init(&style_warning);
    lv_style_set_bg_color(&style_warning, lv_palette_main(LV_PALETTE_YELLOW));
    lv_style_set_border_color(&style_warning, lv_palette_darken(LV_PALETTE_YELLOW, 3));
    lv_style_set_text_color(&style_warning, lv_palette_darken(LV_PALETTE_YELLOW, 4));

    /*Create an object with the base style only*/
    lv_obj_t * obj_base = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj_base, &style_base, 0);
    lv_obj_align(obj_base, LV_ALIGN_LEFT_MID, 20, 0);

    lv_obj_t * label = lv_label_create(obj_base);
    lv_label_set_text(label, "Base");
    lv_obj_center(label);

    /*Create another object with the base style and earnings style too*/
    lv_obj_t * obj_warning = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj_warning, &style_base, 0);
    lv_obj_add_style(obj_warning, &style_warning, 0);
    lv_obj_align(obj_warning, LV_ALIGN_RIGHT_MID, -20, 0);

    label = lv_label_create(obj_warning);
    lv_label_set_text(label, "Warning");
    lv_obj_center(label);
}

#endif

Local styles

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Local styles
 */
void lv_example_style_12(void)
{
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_GREEN));
    lv_style_set_border_color(&style, lv_palette_lighten(LV_PALETTE_GREEN, 3));
    lv_style_set_border_width(&style, 3);

    lv_obj_t * obj = lv_obj_create(lv_screen_active());
    lv_obj_add_style(obj, &style, 0);

    /*Overwrite the background color locally*/
    lv_obj_set_style_bg_color(obj, lv_palette_main(LV_PALETTE_ORANGE), LV_PART_MAIN);

    lv_obj_center(obj);
}

#endif

Add styles to parts and states

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

/**
 * Add styles to parts and states
 */
void lv_example_style_13(void)
{
    static lv_style_t style_indic;
    lv_style_init(&style_indic);
    lv_style_set_bg_color(&style_indic, lv_palette_lighten(LV_PALETTE_RED, 3));
    lv_style_set_bg_grad_color(&style_indic, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_bg_grad_dir(&style_indic, LV_GRAD_DIR_HOR);

    static lv_style_t style_indic_pr;
    lv_style_init(&style_indic_pr);
    lv_style_set_shadow_color(&style_indic_pr, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_shadow_width(&style_indic_pr, 10);
    lv_style_set_shadow_spread(&style_indic_pr, 3);

    /*Create an object with the new style_pr*/
    lv_obj_t * obj = lv_slider_create(lv_screen_active());
    lv_obj_add_style(obj, &style_indic, LV_PART_INDICATOR);
    lv_obj_add_style(obj, &style_indic_pr, LV_PART_INDICATOR | LV_STATE_PRESSED);
    lv_slider_set_value(obj, 70, LV_ANIM_OFF);
    lv_obj_center(obj);
}

#endif

Extending the current theme

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_IMAGE

static lv_style_t style_btn;

/*Will be called when the styles of the base theme are already added
  to add new styles*/
static void new_theme_apply_cb(lv_theme_t * th, lv_obj_t * obj)
{
    LV_UNUSED(th);

    if(lv_obj_check_type(obj, &lv_button_class)) {
        lv_obj_add_style(obj, &style_btn, 0);
    }
}

static void new_theme_init_and_set(void)
{
    /*Initialize the styles*/
    lv_style_init(&style_btn);
    lv_style_set_bg_color(&style_btn, lv_palette_main(LV_PALETTE_GREEN));
    lv_style_set_border_color(&style_btn, lv_palette_darken(LV_PALETTE_GREEN, 3));
    lv_style_set_border_width(&style_btn, 3);

    /*Initialize the new theme from the current theme*/
    lv_theme_t * th_act = lv_display_get_theme(NULL);
    static lv_theme_t th_new;
    th_new = *th_act;

    /*Set the parent theme and the style apply callback for the new theme*/
    lv_theme_set_parent(&th_new, th_act);
    lv_theme_set_apply_cb(&th_new, new_theme_apply_cb);

    /*Assign the new theme to the current display*/
    lv_display_set_theme(NULL, &th_new);
}

/**
 * Extending the current theme
 */
void lv_example_style_14(void)
{
    lv_obj_t * btn;
    lv_obj_t * label;

    btn = lv_button_create(lv_screen_active());
    lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, 20);

    label = lv_label_create(btn);
    lv_label_set_text(label, "Original theme");

    new_theme_init_and_set();

    btn = lv_button_create(lv_screen_active());
    lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -20);

    label = lv_label_create(btn);
    lv_label_set_text(label, "New theme");
}

#endif

Opacity and Transformations

#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BUTTON && LV_USE_LABEL

/**
 * Opacity and Transformations
 */
void lv_example_style_15(void)
{
    lv_obj_t * btn;
    lv_obj_t * label;

    /*Normal button*/
    btn = lv_button_create(lv_screen_active());
    lv_obj_set_size(btn, 100, 40);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, -70);

    label = lv_label_create(btn);
    lv_label_set_text(label, "Normal");
    lv_obj_center(label);

    /*Set opacity
     *The button and the label is rendered to a layer first and that layer is blended*/
    btn = lv_button_create(lv_screen_active());
    lv_obj_set_size(btn, 100, 40);
    lv_obj_set_style_opa(btn, LV_OPA_50, 0);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);

    label = lv_label_create(btn);
    lv_label_set_text(label, "Opa:50%");
    lv_obj_center(label);

    /*Set transformations
     *The button and the label is rendered to a layer first and that layer is transformed*/
    btn = lv_button_create(lv_screen_active());
    lv_obj_set_size(btn, 100, 40);
    lv_obj_set_style_transform_rotation(btn, 150, 0);        /*15 deg*/
    lv_obj_set_style_transform_scale(btn, 256 + 64, 0);   /*1.25x*/
    lv_obj_set_style_transform_pivot_x(btn, 50, 0);
    lv_obj_set_style_transform_pivot_y(btn, 20, 0);
    lv_obj_set_style_opa(btn, LV_OPA_50, 0);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 70);

    label = lv_label_create(btn);
    lv_label_set_text(label, "Transf.");
    lv_obj_center(label);
}

#endif

API

lv_api_map_v8.h

lv_obj_style.h

lv_style_gen.h

lv_style.h