LVGL庫入門教學04-樣式

2022-06-18 12:00:49

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

官方檔案——所有的樣式屬性簡介