返回

LVGL 学习笔记

目录

[]https://www.cnblogs.com/lj15941314/p/17207869.html()

概念

对象(lv_obj_t)

LVGL的三层屏幕

1
2
3
lv_scr_act(void);   // 活动屏幕 disp->act_scr
lv_layer_top (void);    // 顶层       disp->top_layer
lv_layer_sys (void);    // 系统层   disp->sys_layer

基础操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//-----宽高
设置宽度:lv_obj_set_width(obj, new_width);
设置高度:lv_obj_set_height(obj, new_height);

同时设置宽度、高度:lv_obj_set_size(obj, new_ width, new_ height);


获取宽度:lv_obj_get_width(obj);
获取高度:lv_obj_get_height(obj);

//-----位置
设置x轴方向的坐标位置lv_obj_set_x(obj, new_x);
设置y轴方向的坐标位置lv_obj_set_y(obj, new_y);

同时设置xy坐标位置lv_obj_set_pos(obj, new_x, new_y);  // position

获取x轴坐标位置lv_obj_get_x(obj);
获取y轴坐标位置lv_obj_get_y(obj);

屏幕的原点:左上方,x为横轴,y为竖轴

对齐方式

盒子模型

1
2
3
4
5
边界(bounding):元素的宽度/高度围起来的区域(整个盒子)边框(border):边框有大小和颜色等属性(相当于盒子的厚度和它的颜色)填充(padding):对象两侧与其子对象之间的空间(盒子的填充物)内容(content):如果边界框按边框宽度和填充的大小缩小,则显示其大小的内容区域(盒子实际装东西的区域)轮廓(outline) :LVGL中没有外边距(margin)的概念(盒子之间的距离),确认代之的是轮廓(outline)。它是绘制于元素(盒子)周围的一条线,它不占据空间,位于边框边缘的外围,可起到突出元素(盒子)的作用。  例如:在浏览器里,当鼠标点击或使用Tab键让一个选项或者一个图片获得焦点的时候,这个元素就会多了一个轮廓框围绕 。

Widget

标签(lv_label)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
lv_obj_t * label = lv_label_create(parent);


直接设置要显示的文本:lv_label_set_text(label, "New text");
格式化给定要显示的文本:lv_label_set_text_fmt(label, “%s: %d”, “Value”, 15);
文本不存储在动态内存中,而是直接使用给定的缓冲区:lv_label_set_text_static(label, "New text");
在label换行,像printf函数那样使用 \n 即可:lv_label_set_text(label, " line1\nline2\n\nline4 ");

LV_LABEL_LONG_WRAP 如果有多个换行,并且如果高度为LV_SIZE_CONTENT,那么高度会根据文本换行被自动扩展;否则文本将被剪掉。(默认设置)
LV_LABEL_LONG_DOT 如果文本太长,就保持大小并在末尾写3个点。
LV_LABEL_LONG_SCROLL 如果文本比标签宽(太长),则可以水平来回滚动显示它。如果它很高(多个\n换行),可以垂直滚动。只滚动一个方向,水平滚动的优先级更高。LV_LABEL_LONG_SCROLL_CIRCULAR 如果文本比标签宽,则水平滚动它。如果它更高,就垂直滚动。只滚动一个方向,水平滚动的优先级更高。
LV_LABEL_LONG_CLIP 剪掉超出标签范围外的文本部分。
可以使用 lv_label_set_long_mode(label, LV_LABEL_LONG_...) 指定模式。

Label默认不接收输入事件,如果想设置输入类型的样式或者事件会无法生效,需要打开 LV_OBJ_FLAG_CLICKABLE, 示例:lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE); // 使输入设备可点击对象

按钮(lv_btn)

1
2
lv_obj_t * btn = lv_btn_create(parent);
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);

与按键相关的Group Events

开关(lv_switch)

lv_obj_t * sw = lv_switch_create(parent);

Part

  • LV_PART_MAIN 背景。 修改其 padding 会让下面的(指示器)在相应方向上的大小发生变化。
  • LV_PART_INDICATOR 显示开关状态的指示器。
  • LV_PART_KNOB 在指标左侧或右侧的旋钮。 默认情况下,旋钮是圆形的,边长等于滑块的较小边。 可以修改 padding 值使旋钮变大,填充值可以是不对称的。

Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
lv_obj_add_event_cb(switch, event_handler, LV_EVENT_VALUE_CHANGED, NULL);

当开关被打开时,开关的状态会变为 LV_STATE_CHECKED 。我们可以通过下面这两个接口获取开关当前的状态:
    lv_obj_has_state(switch, LV_STATE_CHECKED); // 返回 bool 类型, 开-1 ; 关-2
    lv_obj_get_state(switch); // 返回枚举类型: LV_STATE_...
一般我们通过触摸或按键控制让开关 打开/关闭,还可以通过下面这个接口来主动 打开/关闭:
    lv_obj_add_state(switch, LV_STATE_CHECKED); // 开
    lv_obj_clear_state(switch, LV_STATE_CHECKED); // 关
可以通过下面的接口让按钮处于不可更改状态:
    lv_obj_add_state(sw, LV_STATE_DISABLED); // 当前状态是关,并且不可更改
    lv_obj_add_state(sw, LV_STATE_CHECKED | LV_STATE_DISABLED); // 当前状态是开,并且不可更改
让按钮恢复可以更改的状态,只要将 LV_STATE_DISABLED 清除即可:
    lv_obj_clear_state(switch, LV_STATE_ DISABLED); // 清除禁用状态,按钮可正常使用

Style

Style Docs docs 样式存储在 lv_style_t 变量,样式变量应该是静态 、全局或动态分配的
注意需要及时清空 Style 样式以免造成内存的无效占用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static lv_style_t static_style;
void static_style_demo(void) {
    lv_style_init(&static_style);
    lv_style_set_bg_color(&static_style, lv_color_hex(0xff0000));
    lv_style_set_border_width(&static_style, 2);

    lv_obj_t * btn = lv_btn_create(lv_scr_act());
    lv_obj_add_style(btn, &static_style, LV_STATE_DEFAULT);
}
void dynamic_style_demo(void) {
    lv_obj_t * btn = lv_btn_create(lv_scr_act());

    // 方式1:临时创建样式
    lv_style_t dynamic_style;
    lv_style_init(&dynamic_style);
    lv_style_set_bg_color(&dynamic_style, lv_color_hex(0x00ff00));
    lv_obj_add_style(btn, &dynamic_style, LV_STATE_DEFAULT);

    // 方式2:LVGL 8.3+ 链式API
    lv_obj_add_style(btn, 
        lv_style_builder_create()
        ->bg_color(lv_color_hex(0x0000ff))
        ->border_radius(8)
        ->build(), 
        LV_STATE_PRESSED
    );
}

样式状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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_PART_MAIN 类似矩形的背景
LV_PART_SCROLLBAR 滚动条
LV_PART_INDICATOR 指标,例如用于滑块、条、开关或复选框的勾选框
LV_PART_KNOB 像手柄一样可以抓取调整值
LV_PART_SELECTED 表示当前选择的选项或部分
LV_PART_ITEMS 如果小部件具有多个相似元素(例如表格单元格)
LV_PART_TICKS 刻度上的刻度,例如对于图表或仪表
LV_PART_CURSOR 标记一个特定的地方,例如文本区域或图表的光标
LV_PART_CUSTOM_FIRST 可以从这里添加自定义部件。

Exmaple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void slider_state_parts_demo(void) {
    lv_obj_t * slider = lv_slider_create(lv_scr_act());
    lv_obj_set_size(slider, 200, 30);
    lv_obj_align(slider, LV_ALIGN_CENTER, 0, 0);

    lv_style_t knob_pressed_style;
    lv_style_init(&knob_pressed_style);
    lv_style_set_bg_color(&knob_pressed_style, lv_color_hex(0xFF5722));
    lv_style_set_shadow_width(&knob_pressed_style, 6);

    lv_obj_add_style(slider, 
                     &knob_pressed_style, 
                     LV_PART_INDICATOR | LV_STATE_PRESSED);

    lv_style_t fill_default_style;
    lv_style_init(&fill_default_style);
    lv_style_set_bg_color(&fill_default_style, lv_color_hex(0x4CAF50));

    lv_obj_add_style(slider, 
                     &fill_default_style, 
                     LV_PART_FILL | LV_STATE_DEFAULT);
}

过渡

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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_scr_act());
    lv_obj_add_style(obj, &style_def, 0);
    lv_obj_add_style(obj, &style_pr, LV_STATE_PRESSED);

    lv_obj_center(obj);
}

主题

Events

docs

1
2
3
4
lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL);

uint32_t btn_id = 0;
lv_event_send(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
LV_EVENT_PRESSED 对象已被按下

LV_EVENT_PRESSING 对象被按下(按下时连续调用)

LV_EVENT_PRESS_LOST 对象仍被按下,但光标/手指已滑离对象

LV_EVENT_SHORT_CLICKED 对象被按下一小段时间,然后释放它。如果滚动则不会调用。

LV_EVENT_LONG_PRESSED 对象已按下输入设备驱动程序中指定的至少 long_press_time。如果滚动则不会调用。

LV_EVENT_LONG_PRESSED_REPEAT 在每个 long_press_repeat_time 毫秒的 long_press_time 之后调用。如果滚动则不会调用。

LV_EVENT_CLICKED 如果对象没有滚动,则在释放时调用(无论是否长按)

LV_EVENT_RELEASED 在对象被释放后的每种情况下调用

LV_EVENT_SCROLL_BEGIN 开始滚动。事件参数是 NULL 或 lv_anim_t *,如果需要,可以修改滚动动画描述符。

LV_EVENT_SCROLL_END 滚动结束。

LV_EVENT_SCROLL 对象被滚动

LV_EVENT_GESTURE 检测到手势。使用 lv_indev_get_gesture_dir(lv_indev_get_act()); 获取手势

LV_EVENT_KEY 一个密钥被发送到对象。使用 lv_indev_get_key(lv_indev_get_act()); 获取密钥

LV_EVENT_FOCUSED 对象被聚焦

LV_EVENT_DEFOCUSED 对象散焦

LV_EVENT_LEAVE 对象散焦但仍被选中

LV_EVENT_HIT_TEST 执行高级命中测试。使用 lv_hit_test_info_t * a = lv_event_get_hit_test_info(e) 并检查 a->point 是否可以点击对象。如果没有则 a->res = false

Event bubbling(事件冒泡)

如果启用了 lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE),所有事件也将发送到对象的父级。如果父级也启用了LV_OBJ_FLAG_EVENT_BUBBLE,则事件也将发送到其父级,依此类推。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
static void event_cb(lv_event_t * e)
{
    /*The original target of the event. Can be the buttons or the container*/
    lv_obj_t * target = lv_event_get_target(e);

    /*The current target is always the container as the event is added to it*/
    lv_obj_t * cont = lv_event_get_current_target(e);

    /*If container was clicked do nothing*/
    if(target == cont) return;

    /*Make the clicked buttons red*/
    lv_obj_set_style_bg_color(target, lv_palette_main(LV_PALETTE_RED), 0);
}

/**
 * Demonstrate event bubbling
 */
void lv_example_event_3(void)
{

    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 290, 200);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

    uint32_t i;
    for(i = 0; i < 30; i++) {
        lv_obj_t * btn = lv_btn_create(cont);
        lv_obj_set_size(btn, 80, 50);
        lv_obj_add_flag(btn, LV_OBJ_FLAG_EVENT_BUBBLE);

        lv_obj_t * label = lv_label_create(btn);
        lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
        lv_obj_center(label);
    }

    lv_obj_add_event_cb(cont, event_cb, LV_EVENT_CLICKED, NULL);
}

#endif

Custom events(自定义事件)

任何自定义事件代码都可以通过 uint32_t MY_EVENT_1 = lv_event_register_id(); 注册
并且可以使用 lv_event_send(obj, MY_EVENT_1, &some_data) 发送到任何对象

Refresh event(刷新事件)

LV_EVENT_REFRESH 是一个特殊事件,因为它被设计为用户使用它来通知对象刷新自身。一些例子:

  • 通知标签根据一个或多个变量(例如当前时间)刷新其文本
  • 当语言改变时刷新标签
  • 如果满足某些条件(例如输入正确的 PIN),则启用按钮
  • 如果超出限制,则向/从对象添加/删除样式等

MISC

自定义字体

自带字体:LV_FONT_SIMSUN_16_CJK

转换

字体转换器

常用字体范围

详情见

  • 基础 ASCII: 0x20-0x7F
  • 拉丁扩展:0x80-0xFF
  • 中文字符:0x20-0x7F,0x4E00-0x9FFF
  • FontAwesome 图标:0xf000-0xf2e0

使用

1
2
3
4
5
6
7
8
// 主文件中添加宏
LV_FONT_DECLARE(chili_14);

void setup_style() {
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_text_font(&style, &chili_14); 
}

图标的使用

1
2
3
lv_label_set_text(my_label, LV_SYMBOL_OK);  // 直接显示图标
lv_label_set_text(my_label, LV_SYMBOL_OK Apply);   // 图标与字符串一起使用
lv_label_set_text(my_label, LV_SYMBOL_OK LV_SYMBOL_WIFI LV_SYMBOL_PLAY); // 多个图标一起使用

DrawAPI

i18n

docs
trans
exmaple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include "../../lv_examples.h"

#if LV_USE_TRANSLATION && LV_USE_DROPDOWN && LV_USE_LABEL && LV_BUILD_EXAMPLES

/* Arrays are defined `const` to place them in program space instead of RAM. */
static const char * const tags[] = {"tiger", "lion", "rabbit", "elephant", NULL};
static const char * const languages[] = {"English", "Deutsch", "Español", NULL};

static void add_static_translations(void)
{
    static const char * const translations[] = {
        "The Tiger",    "Der Tiger",     "El Tigre",
        "The Lion",     "Der Löwe",      "El León",
        "The Rabbit",   "Das Kaninchen", "El Conejo",
        "The Elephant", "Der Elefant",   "El Elefante",
    };

    lv_translation_add_static(languages, tags, translations);
}

static void on_language_change(lv_event_t * e)
{
    lv_obj_t * label      = lv_event_get_target_obj(e);
    const char * tag      = (const char *) lv_event_get_user_data(e);
    /* You can get the new language with `lv_event_get_param`*/
    const char * language = (const char *) lv_event_get_param(e);
    LV_UNUSED(language);

    lv_label_set_text(label, lv_tr(tag));
}

static void language_change_cb(lv_event_t * e)
{
    static char selected_lang[20];

    lv_obj_t * dropdown = lv_event_get_target_obj(e);
    lv_dropdown_get_selected_str(dropdown, selected_lang, sizeof(selected_lang));
    lv_translation_set_language(selected_lang);
}

/**
 * Change label text when the translation language changes
 */
void lv_example_translation_2(void)
{
    lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);

    add_static_translations();
    const size_t tag_count = sizeof(tags) / sizeof(tags[0]) - 1;
    const size_t lang_count = sizeof(languages) / sizeof(languages[0]) - 1;

    /* Create a dropdown to be able to select the language */
    lv_obj_t * language_dropdown = lv_dropdown_create(lv_screen_active());
    lv_dropdown_clear_options(language_dropdown);

    for(size_t i = 0; i < lang_count; ++i) {
        lv_dropdown_add_option(language_dropdown, languages[i], i);
    }

    lv_obj_add_event_cb(language_dropdown, language_change_cb, LV_EVENT_VALUE_CHANGED, NULL);

    /* Create a label for each tag */
    for(size_t i = 0; i < tag_count; ++i) {
        lv_obj_t * label = lv_label_create(lv_screen_active());

        /* Bind to the language change event so that we can change the label when the language changes */
        lv_obj_add_event_cb(label, on_language_change, LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, (void *)tags[i]);
    }

    lv_translation_set_language("English");
}

#endif /*LV_USE_TRANSLATION && LV_USE_DROPDOWN && LV_USE_LABEL && LV_BUILD_EXAMPLES*/
Licensed under CC BY-NC-SA 4.0