我的LVGL9.3学习笔记

最近学习了lvgl,感觉上手起来比较容易,但是部件的内容有点多,个人感觉细节方面也很多,所以做个笔记,供自己忘了就来查看与迅速回忆,同时记录自己遇到的一些细节问题

笔记其实不太详细,是针对我个人的,记载感觉自己会容易忘记的部分内容以及自己跟着教程学的时候,教程没有提及但是自己发现了的问题,

lvgl版本:9.3

开发环境:VScode在windows平台模拟lvgl(VScode不愧为宇宙级编辑器,搭配插件感觉非常适合用来模拟lvgl)

部件太多,所以只列举部分

对齐方式

此处贴一张图片即可说明所有的对齐方式:(图片摘自正点原子)

注意:

如果同时存在设置部件大小与对齐方式,部件对齐方式一定要后于部件大小设置,否则对齐方式会受到影响,结果达不到预期效果

代码说明:要先设置好lv_obj_set_size,再设置lv_obj_align或者lv_obj_align_to!!!

lv_obj_set_size

lv_obj_align / lv_obj_align_to

子对象与父对象

一般的部件,均已当前屏幕作为父对象,子对象的显示区域会受到父对象的显示区域的影响

子对象的显示区域如果超过父对象,超出的区域会被父对象遮挡,父对象会生成拖动框来供用户拖动以查看子对象被父对象遮挡的部分

使用对齐方式有两种:lv_obj_align_to与lv_obj_align,

lv_obj_align_to:适用于两个没有父子关系的部件,其中一个部件以另一个部件为基准进行对齐

lv_obj_align:适用于两个存在父子关系的部件,子对象会以父对象为基准,再父对象内部进行对齐

通过实验,得出以下结论:

倘若使用了lv_obj_align,且父对象是当前屏幕,且参数使用的外部对齐,虽然程序不会报错,但是是界面会显示子对象处于父对象内部左上对齐,不符合预期 

倘若使用了lv_obj_align_to,参数的两个对象具有父子关系,并且使用的外部对齐,程序不报错,但是无法显示子对象

标签部件(label)

功能

标签部件用于显示文本内容,也可以显示lvgl自带的图标(SYMBOL),通过声明字库,也可以显示中文

设置文本内容

两种方式:

纯文本:

lv_label_set_text(label4, "你好我是丁真");

可以显示变量数值(类似于printf):

lv_label_set_text_fmt(label2, "label value:%d", 500);

长文本模式

正常情况下,标签部件的尺寸大小会随着所要显示的文本内容的长度进行动态变化,如果要使用lv_obj_set_size来限制标签部件的大小,那么所要显示的文本内容就会超过标签部件的大小,这时候就要使用标签部件的长文本模式

lv_label_set_long_mode(label1, LV_LABEL_LONG_MODE_SCROLL_CIRCULAR);

对于第二个参数,注释如下:

typedef enum {
    LV_LABEL_LONG_MODE_WRAP,             /**< 保持控件宽度不变,自动换行显示长文本,控件高度会根据内容自动扩展 */
    LV_LABEL_LONG_MODE_DOTS,             /**< 保持控件尺寸不变,当文本过长时在末尾显示省略号(...) */
    LV_LABEL_LONG_MODE_SCROLL,           /**< 保持控件尺寸不变,文本会在控件内来回滚动显示 */
    LV_LABEL_LONG_MODE_SCROLL_CIRCULAR,  /**< 保持控件尺寸不变,文本会以循环滚动的方式持续显示 */
    LV_LABEL_LONG_MODE_CLIP,             /**< 保持控件尺寸不变,直接裁剪超出控件区域的文本部分 */
} lv_label_long_mode_t;

需要注意的是:

使用滚动模式的时候,如果标签的宽度足够容纳文本,但是高度不够,那么滚动方向就会变为垂直向上滚动

如果标签的高度足够容纳文本,但是宽度不够,那么滚动方向就会变为水平向左滚动

文本颜色

上代码

 //设置标签所有本文颜色
    lv_obj_set_style_text_color(label2, lv_color_hex(0xa10ffc), LV_STATE_DEFAULT);
//设置标签部分文本颜色,使用此功能,一定要搭配使能重新着色函数,先使能重新着色
    lv_label_set_recolor(label1, true);
    lv_label_set_text(label1, "Hello\n #ff8536 worl#d");

需要注意的是,部分文本着色时,一定要先使能重新着色函数,否则LVGL 会将颜色标记视为普通文本的一部分,而不会将其解释为颜色指令

文本字体大小

上代码

 //设置文本字体大小,注意字体宏定义是大写,但是实际传入的是小写加取地址符,同时注意对应序号的字体使能为1
    lv_obj_set_style_text_font(label1, &lv_font_montserrat_26, LV_STATE_DEFAULT);

设置字体,首先要将对应字体(lv_conf.h文件)使能,传入参数的时候注意传参方式

/* Montserrat fonts with ASCII range and some symbols using bpp = 4
 * https://fonts.google.com/specimen/Montserrat */
#define LV_FONT_MONTSERRAT_8  0
#define LV_FONT_MONTSERRAT_10 0
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 1
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_22 1
#define LV_FONT_MONTSERRAT_24 1
#define LV_FONT_MONTSERRAT_26 1
#define LV_FONT_MONTSERRAT_28 1
#define LV_FONT_MONTSERRAT_30 1
#define LV_FONT_MONTSERRAT_32 1
#define LV_FONT_MONTSERRAT_34 1
#define LV_FONT_MONTSERRAT_36 1
#define LV_FONT_MONTSERRAT_38 1
#define LV_FONT_MONTSERRAT_40 1
#define LV_FONT_MONTSERRAT_42 1
#define LV_FONT_MONTSERRAT_44 1
#define LV_FONT_MONTSERRAT_46 1
#define LV_FONT_MONTSERRAT_48 1

文本重影效果

如图:

若想要实现这种文本,只需要设置两个标签,先创建灰色(重影背景)标签,再创建紫色(主显示)标签(顺序不可调换),再通过分别设置颜色,以及对齐方式调整,即可显示出此效果

原因如下:

LVGL的层级因素:创建的一个部件如果显示区域与另一个部件有重叠,最后创建的部件会遮挡最先创建的,这就是LVGL的层级影响(摘自AI,通过实际的实验得出的效果也是如此)

代码如下:(紫色是label2, 灰色是label3)

lv_obj_t* label3 = lv_label_create(lv_screen_active());
lv_obj_t* label2 = lv_label_create(lv_screen_active());

lv_obj_align_to(label2, label1, LV_ALIGN_OUT_BOTTOM_LEFT, 20, 100);
lv_label_set_text_fmt(label2, "label value:%d", 500);
lv_obj_align_to(label3, label2, LV_ALIGN_TOP_LEFT, 3, 3);
lv_label_set_text_fmt(label3, "label value:%d", 500);

lv_obj_set_style_text_color(label2, lv_color_hex(0xa10ffc), LV_STATE_DEFAULT);
lv_obj_set_style_text_color(label3, lv_color_hex(0xB9C2C0), LV_STATE_DEFAULT);

按钮部件(button)

对于按钮,本人觉得最值得注意的就是按钮的样式设置,按钮还是有很多部位可以进行样式设置的:

事件

按钮常用事件是点击事件或者值改变事件:LV_EVENT_VALUE_CHANGED(值改变)   与 LV_EVENT_CLICKED(点击)

普通按钮点击后只触发一次事件,而可勾选按钮在点击时会在 "按下" 和 "释放" 两种状态之间切换。每次点击都会改变按钮的状态。

按钮可以通过添加值改变LV_OBJ_FLAG_CHECKABLE状态,来增加按钮的可勾选状态

lv_obj_add_flag(button_stop, LV_OBJ_FLAG_CHECKABLE);

样式

下命列出按钮9可以设置的样式例子:

//设置背景透明度
lv_obj_set_style_bg_opa(msgbox_continue_btn, LV_OPA_0, LV_STATE_DEFAULT);

lv_obj_set_style_bg_opa(msgbox_continue_btn, LV_OPA_30, LV_STATE_PRESSED);
//设置背景颜色
lv_obj_set_style_bg_color(msgbox_continue_btn, lv_color_hex(0xA3A2A2), LV_STATE_PRESSED);
//设置按钮内部文本颜色
lv_obj_set_style_text_color(msgbox_continue_btn, lv_color_hex(0x0090FE), LV_STATE_DEFAULT);
//设置按钮阴影宽度
lv_obj_set_style_shadow_width(msgbox_continue_btn, 5, LV_STATE_PRESSED);

lv_obj_set_style_shadow_width(msgbox_continue_btn, 0, LV_STATE_DEFAULT);

事件回调(event)

标准的事件回调函数头:

void fast_button_event(lv_event_t* event)

当为某个部件添加事件回调函数以及触发事件,当事件发生的时候,再回调函数内就可以通过

lv_obj_t* target = lv_event_get_target(event);与

lv_event_code_t code = lv_event_get_code(event);

分别获取触发源与事件

区分:

lv_obj_t* target = lv_event_get_target(event);是用来获取触发源的也就是是哪一个部件触发了事件回调,比如,创建了多个按钮部件,当lvgl检查到有按钮被点击,但是不知道是哪一个按钮被点击了,就可以通过获取触发源来判断是哪一个按钮被点击了

lv_event_code_t code = lv_event_get_code(event);是用来获取事件的,同一个部件是可以存在多个触发事件的(比如LV_EVENT_FOCUSED与LV_EVENT_DEFOCUSED),如果单独判断触发源还远远不够,所以要加上触发事件,两者组合,就可以完美的区分触发源加触发事件了

开关部件(switch)

特点

开关是由主体,指示器,旋钮三个部分组成

且开关部件的默认状态是关闭的,此时,主体部分会覆盖指示器部分

开关状态是开启的时候,指示器的部分也会覆盖主体部分

样式

设置开关部件的指示器颜色的时候,根据前面的叙述,最好是在开关处于开启状态的时候组合设置

也就是,设置为开启状态的时候显示,同时设置指示器的颜色,代码如下

lv_obj_set_style_bg_color(switch1, lv_color_hex(0x25FC04), LV_PART_INDICATOR | LV_STATE_CHECKED);

状态

开关的状态分为默认状态与开启状态(LV_STATE_CHECKED)

同时,可以通过

lv_obj_set_state(dry_switch, LV_STATE_CHECKED | LV_STATE_DISABLED, true);

来设置为:开关默认为开启状态(LV_STATE_CHECKED),并且这个状态不可以被改变(LV_STATE_DISABLED)

也可以通过

lv_obj_has_state(hot_switch, LV_STATE_CHECKED)

来查看开关是否处于某一特定状态

事件

开关部件的触发事件为值改变

lv_obj_add_event_cb(cold_switch, my_cold_switch_event, LV_EVENT_VALUE_CHANGED, NULL);

进度条部件(bar)

设置进度条的范围数值

lv_bar_set_range(bar1, -100, 100);

设置进度条大小尺寸

需要注意的是:当进度条宽度小于长度,方向变为垂直方向

lv_obj_set_size(bar1, 200, 20);

(垂直方向的进度条)

设置进度条当前数值(默认)

lv_bar_set_value(bar1, -100, false);

进度条默认状态是false,即不加载进度,zhi'hu

设置动画效果

lv_obj_set_style_anim_duration(bar1, 2000, LV_STATE_DEFAULT);

设置进度条当前数值

lv_bar_set_value(bar1, 100, true);

设置进度条动画效果,其作用是将进度条从默认数值(如果没有改变就是0)移动到所设置的当前数值。

如果需要显示进度条从指定数值增长到目标值这一过程,只需要在lv_obj_set_style_anim_duration语句前后分别设置false的值与true的值即可,并且动画效果函数一定要放在设置当前值(状态是true,也可以叫目标值)函数之前,否则看不到动画效果

LVGL的定时器

lvgl自带定时器,可以通过创建函数创建定时器

//创建LVGL自带的软件定时器
    lv_timer_t* my_timer = lv_timer_create(timer_cb, 100, NULL);

lv_timer_create 函数用于创建一个新的 lv_timer 定时器。它接受一个回调函数 timer_xcb、定时间隔 period(以毫秒为单位)以及用户数据 user_data 作为参数,并返回一个指向新创建的 lv_timer_t 的指针

定时器回调函数

/*软件定时器回调函数*/
void timer_cb(lv_timer_t* timer)
{


}

复选框部件(checkbox)

设置选项对应文本内容

lv_checkbox_set_text(checkbox1, "Fish: 15 rmb");

每个复选框部件对应一个选项

设置复选框状态

lv_obj_set_state(checkbox5, LV_STATE_DISABLED | LV_STATE_CHECKED, true);

列表部件(list)

列表的每一个选项都是一个按钮部件,所以很简单就可以想到,列表部件对应的触发事件,就是按钮的点击CLICK事件

list_button1 = lv_list_add_button(list1, LV_SYMBOL_WIFI, "WLAN");
lv_obj_add_event_cb(list_button1, list_button1_event, LV_EVENT_CLICKED, NULL);

获取列表的文本项

 lv_list_get_button_text(list1, list_button1);

添加列表文本项,其文本项本质是label部件

lv_obj_t* list_label = lv_list_add_text(list1, "Setting");

下拉列表部件(dropdown)

设置选项文本内容

每个选项均已“\n”字符分割

lv_dropdown_set_options(dropdown1, "Gen\nshin\nimpact");

添加选项及其索引

lv_dropdown_add_option(dropdown1, "Hello", 0);

默认显示选项

lv_dropdown_set_selected(dropdown1, 1);

设置展开的方向

lv_dropdown_set_dir(dropdown1, LV_DIR_RIGHT);

根据展开的方向设置图标

lv_dropdown_set_symbol(dropdown1, LV_SYMBOL_BARS);

事件

下拉列表对应的触发事件是LV_EVENT_VALUE_CHANGED,值改变

获取当前选项索引

int i1 = 0;
i1 = lv_dropdown_get_selected(dropdown1);

获取当前选项文本内容

char dropdown_text[10] = "";
lv_dropdown_get_selected_str(dropdown1, dropdown_text, sizeof(dropdown_text));

滚轮部件(roller)

设置各个选项的内容与滚动模式

lv_roller_set_options(roller1, "option 1\noption 2\noption 3\noption 4\noption 5", LV_ROLLER_MODE_NORMAL);

其中滚动模式可以选择无限滚动(循环滚动)与单词滚动(滚动到末尾即止)

设置当前所选项

lv_roller_set_selected(roller1, 2, LV_STATE_DEFAULT);

设置滚轮可见的行数

lv_roller_set_visible_row_count(roller1, 3);

获取当前选项文本内容

char roller_buf[10] = "";
lv_roller_get_selected_str(roller1, roller_buf, sizeof(roller_buf));

获取当前所选选项的索引号

unsigned char roller_Selected = 0;
roller_Selected = lv_roller_get_selected(roller1);

设定滚轮不可滑动

通过添加DISABLE状态即可实现

lv_obj_add_state(roller1, LV_STATE_DISABLED);

事件

lv_obj_add_event_cb(roller1, roller_event, LV_EVENT_VALUE_CHANGED, NULL);

滚轮部件的触发事件为值改变

滑块部件(silder)

三个模式

滑块部件可以设置为三个状态:

LV_SLIDER_MODE_NORMAL:默认的模式

LV_SLIDER_MODE_SYMMETRICAL :无论当前值是正数还是负数,指示器始终从零
绘制到当前值。
LV_SLIDER_MODE_RANGE :允许调用 lv_bar_set_start_value 函数设置起始值,该起
始值必须小于结束值。

设置数值范围

lv_slider_set_range(slider0, 0, 100);

设置当前数值

lv_slider_set_value(slider0, 80, LV_ANIM_ON);

动画效果

lv_slider_set_value(slider0, 0, LV_ANIM_OFF);
lv_obj_set_style_anim_duration(slider0, 2000, LV_STATE_DEFAULT);
lv_slider_set_value(slider0, 80, LV_ANIM_ON);

获取当前数值

lv_slider_get_value(slider0);

获取最大值

lv_slider_get_max_value(slider2);

获取最小值

lv_slider_get_min_value(slider2);

事件

滑块部件的触发事件是值改变:

lv_obj_add_event_cb(slider2, slider_event, LV_EVENT_VALUE_CHANGED, NULL);

圆弧部件(arc)

设置取值范围

lv_arc_set_range(arc2, 0, 100);

设置当前数值

lv_arc_set_value(arc1, 0);

背景弧,前景弧

背景弧是灰色部分的角度,前景弧是指示器(蓝色区域的角度)

设置圆弧的角度的时候,圆弧的是角度以3点钟方向为0°,顺时针方向角度逐步增加,再次回到3点钟方向为360°

并且需要注意,前景弧的角度范围不能超过背景弧的角度范围,否则背景弧的角度范围就会限制前景弧的角度范围,并且要先设置背景弧再设置前景弧

对于圆弧部件,角度和值设置是独立的,应该只使用其中一种。将两者混合在一起可能会导致意外行为(官方提醒)

而且通过实测得知,对于前景弧,如果已经使用过lv_arc_set_value,其作用会覆盖lv_arc_set_angles的值,所以建议不要使用设置前景弧角度函数

对于第一个圆弧

背景弧角度为360°

lv_arc_set_bg_angles(arc1, 0, 360);

根据用户改变的第一个圆弧的数值,设置直接通过设置当前数值函数改变圆弧2部件数值即可

lv_arc_set_value(arc2, arc_value);

绘制模式

默认情况下,圆弧部件是沿顺时针方向绘制的,如果需要修改绘制的方向,可以调用
lv_arc_set_mode 函数,设置圆弧的绘制模式,圆弧模式相关的枚举如下所示:
LV_ARC_MODE_NORMAL :顺时针方向绘制;
LV_ARC_MODE_REVERSE :逆时针方向绘制;
LV_ARC_MODE_SYMMETRIC:从中间点开始绘制到当前值。
lv_arc_set_mode(arc1, LV_ARC_MODE_NORMAL);

逆时针模式

顺时针模式(默认)

中间点绘制(当前值是0,中间值为50)

lv_arc_set_range(arc1, 0, 100);

变化速率

当圆弧的旋钮被滑动时,前景弧将根据设定的变化率来绘制。变化率的单位为:度 / 秒,
lv_arc_set_change_rate(arc1, 90); 

事件

圆弧部件的触发事件为值改变

lv_obj_add_event_cb(arc1, arc_event, LV_EVENT_VALUE_CHANGED, NULL);

线条部件(line)

设置线条的点坐标

static lv_point_precise_t my_points[] = {{150, 50}, {250, 200}, {50, 200}, {150, 50}};
lv_line_set_points(line1, my_points, 4);

设置线条宽度

lv_obj_set_style_line_width(line1, 2, LV_STATE_DEFAULT);

设置交界处的圆角特效

lv_obj_set_style_line_rounded(line1, true, LV_STATE_DEFAULT);

Y轴翻转

Y 操作指的是将线条部件的参考原点设置到左下角,其最终效果相当于将线条沿 Y 轴方向镜像翻转
lv_line_set_y_invert(line1, true);

图片部件(image)

指定图片路径

LV_IMAGE_DECLARE(fan);

显示自定义图片

lv_image_set_src(image1, &fan);

显示图片方法:

1.找一个图片(格式不限),不过感觉需求最多的还是想找图标,这里推荐一个图标网站:

iconfont-阿里巴巴矢量图标库

2.去到lvgl专用在线转换网站转换:Image Converter — LVGL,正点原子的那个方法,在lvgl9.3版本不适用(实测),新版本应该直接选择RGB565格式,如图

3.得到.c文件后,进入文件,把里面的0x00全部换成0xff进行反相显示(我的如果不这么做,图片是黑色的,所以反其道而行之,0x00换成0xff就可以了)

缺点就是不能单独调节图片线条的颜色,背景颜色会随之一起改变,而且突出的白色背景无法实现透明度设置,我反正是懒得改了,就这样吧,能显示就行

设置图片x轴偏移

lv_image_set_offset_x(image1, 20);

设置图片y轴偏移

lv_image_set_offset_y(image1, 20);

图片缩放

lv_image_set_scale(image1, 256);

256是原尺寸,512放大两倍,128缩小两倍,以此类推

图片旋转

lv_image_set_rotation(image1, 900);

900是顺时针90°,1800是顺时针180°,直到3600

选项卡部件(tabview)

创建选项

其中每一个选项都要对应创建一个tab,附带选项文本内容

lv_obj_t* tab1 = lv_tabview_add_tab(tab_view1, "Genshin");

设置选项的文本内容

只需要以创建选项所得到的对象为父对象创建标签部件即可

lv_obj_t* tab1 = lv_tabview_add_tab(tab_view1, "Genshin");
lv_obj_t* tab1_label = lv_label_create(tab1);
lv_label_set_text(tab1_label, "wo ai wan yuanshen!");
lv_obj_align(tab1_label, LV_ALIGN_TOP_MID, 0, 0);

设置选项卡的方向

方向默认是TOP

lv_tabview_set_tab_bar_position(tab_view1, LV_DIR_TOP);

设置选项按钮的高度

lv_tabview_set_tab_bar_size(tab_view1, 50);

这里引入一张正点原子的图片:

lv_obj_update_layout()的理解

在学习的时候,发现这个函数有时候若有若无的出现在教程,有的时候说一定要使用,有的时候却又没有见到这个函数的身影,所以特别研究了一下这个函数的用法与功能

先附上AI的解答(豆包)

为此,本人特别做了一次实验,实验代码如下

void layout_test(void)
{
    lv_obj_t* show_label_before = lv_label_create(lv_screen_active());
    lv_obj_t* show_label_after = lv_label_create(lv_screen_active());
    lv_obj_align_to(show_label_after, show_label_before, LV_ALIGN_BOTTOM_MID, 0, 20);
    
    lv_obj_t* test_label = lv_label_create(lv_screen_active());
    lv_label_set_text(test_label, "this is the test_label");
    lv_obj_align(test_label, LV_ALIGN_CENTER, 0, 0);
    lv_label_set_text_fmt(show_label_before, "before is %d", lv_obj_get_x(test_label));

    lv_obj_update_layout(test_label);
    lv_label_set_text_fmt(show_label_after, "after is %d", lv_obj_get_x(test_label));
}

结果如图:

可见,在我们调用lv_obj_update_layout之前,即使我们将标签移到了屏幕的中间位置,其获取坐标依然没有更新,还是0

但是当我们调用了lv_obj_update_layout之后,再获取坐标,其坐标才会更新

所以,只要涉及获取坐标的操作,一定要加上lv_obj_update_layout

平铺视图部件(tileview)

添加页面

lv_obj_t* tile1 = lv_tileview_add_tile(tileview1, 0, 0, LV_DIR_RIGHT);
lv_obj_t* tile2 = lv_tileview_add_tile(tileview1, 1, 0, LV_DIR_LEFT);

其中第二个参数为列,第三参数为行,tile1为0列0行,tile1为1列0行,最开始的页面的第四个参数可以根据要求设置,假如是LV_DIR_RIGHT,意思就是其他视图可以通过向右滑动切换到tile1,lvgl在这里的设置还挺反逻辑的

也可以是LV_DIR_RIGHT | LV_DIR_BOTTOM,方向可以通过 | l来进行多重方向设置的

或者举一个生动点的例子,初始视图坐标都是(0, 0),位于第二象限,假如在此基础上创建了第三象限的视图,并且要求二三象限的视图可以来回切换,此时设置就应该为:

lv_obj_t* tile1 = lv_tileview_add_tile(tileview1, 0, 0, LV_DIR_BOTTOM);
lv_obj_t* tile3 = lv_tileview_add_tile(tileview1, 0, 1, LV_DIR_TOP);

在上面的基础上,加上第一象限,二三象限可以来回切换,一二象限可以来回切换,代码为:

lv_obj_t* tile1 = lv_tileview_add_tile(tileview1, 0, 0, LV_DIR_RIGHT | LV_DIR_BOTTOM);
lv_obj_t* tile2 = lv_tileview_add_tile(tileview1, 1, 0, LV_DIR_LEFT);
lv_obj_t* tile3 = lv_tileview_add_tile(tileview1, 0, 1, LV_DIR_TOP);

其他形式的就依此设定即可

添加页面主体内容

lv_obj_t* tile1 = lv_tileview_add_tile(tileview1, 0, 0, LV_DIR_RIGHT);
lv_obj_t* tileview1_label = lv_label_create(tile1);
lv_label_set_text(tileview1_label, "page1")

通过之前添加页面返回的对象为父对象,创建标签并添加内容即可

切换动画

切换动画之前一定要加上更新布局的函数,同时注意在动画函数后面加上要显示的页面对象即可

//更新部件的布局
lv_obj_update_layout(tileview1);
//开启动画切换效果
lv_obj_set_style_anim_duration(tileview1, 2000, LV_STATE_DEFAULT);
lv_tileview_set_tile(tileview1, tile2, LV_ANIM_ON);

因为平铺视图产生动画时,随着动画的进行,里面的所有部件的坐标都会改变,这就触发了坐标的实时更新这一特点,自然就会联想到需要使用lv_obj_update_layout了

由于滑动平铺视图,当前页面都会跟着滑动,如何才能像手机那样,顶部的特定区域的部件不随之滑动呢?很简单,只需要额外创建部件,并且是以当前屏幕为父对象即可

lv_obj_t* tileview1_label2 = lv_label_create(lv_screen_active());
lv_obj_align(tileview1_label2, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_t* tileview1_label3 = lv_label_create(lv_screen_active());
lv_obj_align(tileview1_label3, LV_ALIGN_TOP_LEFT, 0, 0);
lv_label_set_text(tileview1_label2, LV_SYMBOL_WIFI"  80% "LV_SYMBOL_BATTERY_3);
lv_label_set_text(tileview1_label3,"  08:30 AM");

效果如图:

按钮矩阵部件(buttonmatrix)

设置按钮个数与文本内容

const char* buttonmatrix_map[] = {"1", "2", "3", "\n", "4", "5", "6", "\n", "7", "8", "9", "\n", "*", "0", "#", ""};
lv_obj_t* buttonmatrix1 = lv_buttonmatrix_create(lv_screen_active());
lv_buttonmatrix_set_map(buttonmatrix1, buttonmatrix_map);

需要注意的是,设置按钮的文本的时候,最后一个元素必须为空:" ",并且切换行的时候以"\n"分割

设置相对宽度

lv_buttonmatrix_set_button_width(buttonmatrix1, 1, 2);

第二个参数为按钮的索引号,第三参数为倍数,此函数是相对宽度,以此举例:索引为1的按钮的宽度设置为其前一个按钮,也就是索引为0的按钮的宽度的两倍

设置某一个按键的属性

lv_buttonmatrix_set_button_ctrl(buttonmatrix1, 0, LV_BUTTONMATRIX_CTRL_RECOLOR);

设置所有按键的属性

lv_buttonmatrix_set_button_ctrl_all(buttonmatrix1, LV_BUTTONMATRIX_CTRL_CHECKABLE);

按钮互斥

以下摘自正点原子

按钮互斥是指:在某一时刻,只允许有一个按钮处于按下不弹起状态(被选中),当我们
选中一个按钮之后,其他的按钮将会自动清除选中属性
lv_buttonmatrix_set_one_checked(buttonmatrix1, true);

使用此函数的时候,要先赋予按钮的可切换状态属性

文本区域部件(textarea)

文本区域部件就是一个文本输入框, 用户可以在该部件的文本区域中输入字符和删除字符

设置文本内容

lv_textarea_add_char(textarea1, 'X');

lv_textarea_add_text(textarea1, "yuanshen6668468464564646468468464367 \nwo ai wan yuanshen\nni jiu shi ge jiba");

既可以单字符,也可以字符串

删除字符

文本区域部件删除字符的方法有两种:
从光标位置的左侧删除一个字符
lv_textarea_delete_char(textarea1);
从光标位置的右侧删除一个字符
lv_textarea_delete_char_forward(textarea1);

设置占位符

占位符 即默认显示的文本,常用于对用户进行默认的提示、说明或引导
lv_textarea_set_placeholder_text(textarea1, "genshin:");

限制可接收字符

lv_textarea_set_accepted_chars(textarea1, "0123456789");

文本框只可以出现双引号里面的任意一个字符

限制可接收字符的长度

文本框里面最多可以显示6个字符:

lv_textarea_set_max_length(textarea1, 6);

关联键盘

文本区域部件常与键盘部件搭档

lv_obj_t* textarea_keyboard = lv_keyboard_create(lv_screen_active());
lv_keyboard_set_textarea(textarea_keyboard, textarea1);

设置光标位置

文本区域的光标默认在左上角的位置。当添加一个字符时,该光标从左往右移动,默认情况下,光标会一直在文本最后一个字符的右侧。但是会出现需要特别设置光标的位置:

lv_textarea_set_cursor_pos(textarea1, LV_TEXTAREA_CURSOR_LAST);

LV_TEXTAREA_CURSOR_LAST表示最右侧

0就是最左侧

也可以单独赋值数字,代表光标出现在第几个字符的位置

特殊模式

单行模式:默认情况下,文本区域的文本超出它的宽度时,该文本将自动换行。如果将文本区域部件设置为单行模式,当它的文本超出其宽度后,文本并不会自动换行,超出的文本将水平滚动显示

lv_textarea_set_one_line(textarea1, true);

密码模式:为了保证文本的机密性,LVGL 的文本区域部件为用户提供了密码模式,当用户输入文本之后,这些文本内容将以“*”字符替代

lv_textarea_set_password_mode(textarea1, true);
当使用密码模式时,原始文本会先显示一段时间,然后隐藏,该显示时间可以通过下面的函数数字,单位是ms:
lv_textarea_set_password_show_time(textarea1, 100);

获取文本内容

如果用户想获取密码框的文本内容,可调用 lv_textarea_get_text 函数,返回的文本并不是“*”字符,而是原始的文本内容。

const char* textarea_text = "";
textarea_text = lv_textarea_get_text(textarea1);

对比文本内容

if(strcmp("123456", textarea_text) == 0)
{
    lv_label_set_text(textarea_label, "pass!");
}

使用的是string.h里面的strcmp函数,如果对比的两给字符串相同,返回0

事件

文本区域部件的常用事件是值改变,与键盘搭配使用的事件见键盘部件

lv_obj_add_event_cb(textarea1, textarea_event, LV_EVENT_VALUE_CHANGED, NULL);

键盘部件(keyboard)

弹窗提示

lv_keyboard_set_popovers(keyboard1, true);

设置模式

键盘部件内嵌了多种输入模式:大写字母、小写字母和特殊字符等,用户可以触摸相应的按键进行模式切换

lv_keyboard_set_mode(keyboard1, LV_KEYBOARD_MODE_TEXT_LOWER);

其实默认模式就行,只要按下相应按键,都可以在这几个模式切换

关联文本区域

键盘部件没有与任何输入框关联,此时,即使用户输入内容,这些内容并不会更新到输入框中,因此,如果我们想要把键盘输入的内容传入在文本区域部件当中,就必须调用 lv_keyboard_set_textarea 函数,把键盘和文本区域部件关联起来

lv_keyboard_set_textarea(keyboard1, textarea_to_keyboard1);

注意的是,一个键盘只能关联一个文本框,所以需要为文本框添加事件,在事件中切换键盘关联的文本框的部件

事件

常用搭配如下:

lv_obj_add_event_cb(textarea_to_keyboard2, keyboard_event, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(textarea_to_keyboard1, keyboard_event, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(keyboard1, keyboard_event, LV_EVENT_ALL, NULL);

由于文本区域在实验中同时存在LV_EVENT_FOCUSED与LV_EVENT_DEFOCUSED状态,实验干脆填上LV_EVENT_ALL,在事件回调函数再另行判断

事件回调代码:

lv_obj_t* textarea_to_keyboard1;
lv_obj_t* textarea_to_keyboard2;
lv_obj_t* keyboard1;
lv_obj_t* keyboard_label;
const char* textarea_to_keyboard1_1 = "";
const char* textarea_to_keyboard1_2 = "";
void keyboard_event(lv_event_t* event)
{
    lv_obj_t* target = lv_event_get_target(event);
    lv_event_code_t code = lv_event_get_code(event);
    if(target == textarea_to_keyboard1 || target == textarea_to_keyboard2)
    {
        if(code == LV_EVENT_FOCUSED)
        {
            lv_obj_remove_flag(keyboard1, LV_OBJ_FLAG_HIDDEN);  
            lv_keyboard_set_textarea(keyboard1, target);
        }
        else if(code == LV_EVENT_DEFOCUSED)
        {
            lv_obj_add_flag(keyboard1, LV_OBJ_FLAG_HIDDEN);
            lv_keyboard_set_textarea(keyboard1, NULL);
        }     
    }
    if(target == keyboard1)
    {
        if(code == LV_EVENT_READY)
        {
            textarea_to_keyboard1_1 = lv_textarea_get_text(textarea_to_keyboard1);
            textarea_to_keyboard1_2 = lv_textarea_get_text(textarea_to_keyboard2);
            if(  strcmp(textarea_to_keyboard1_1, "xieyuan") == 0 && 
                strcmp(textarea_to_keyboard1_2, "300200100") == 0  )
            {
                lv_label_set_text(keyboard_label, "Genshin666!!!");
            }
            else 
            {
                lv_label_set_text(keyboard_label, "SB!!!");
            }
        }
        else if(code == LV_EVENT_CANCEL)
        {  
            lv_keyboard_set_textarea(keyboard1, NULL);
            lv_obj_add_flag(keyboard1, LV_OBJ_FLAG_HIDDEN);
        }
    }
}

效果是像手机那样,只要鼠标点击文本框以外的区域,键盘隐藏,再次点击文本框的时候,键盘恢复显示

同时,当按下键盘的cancel按钮的时候,也需要隐藏键盘(参考的lvgl官网例程)

cancel按钮如下:

图片按钮部件(imagebutton)

在图片按钮部件中,每一个图片都要对应一个按钮部件

设置图片跟图片部件的步骤一样

设置图片按钮的某个状态下所要显示的图片

lv_imagebutton_set_src(imgbtn_snow, LV_IMAGEBUTTON_STATE_RELEASED, NULL, &snow_off, NULL);
lv_imagebutton_set_src(imgbtn_snow, LV_IMAGEBUTTON_STATE_PRESSED, NULL, &snow_on, NULL);

lv_imagebutton_set_src(imgbtn_hot, LV_IMAGEBUTTON_STATE_RELEASED, NULL, &hot_off, NULL);
lv_imagebutton_set_src(imgbtn_hot, LV_IMAGEBUTTON_STATE_PRESSED, NULL, &hot_on, NULL);
    
lv_imagebutton_set_src(imgbtn_wind, LV_IMAGEBUTTON_STATE_RELEASED, NULL, &wind_off, NULL);
lv_imagebutton_set_src(imgbtn_wind, LV_IMAGEBUTTON_STATE_PRESSED, NULL, &wind_on, NULL);

只需要设置按下与松开(默认)状态下的不同颜色显示的图片,就可以达到图片似乎被按下的效果,

这里正点原子是采用了在事件回调函数里面实现图片的重新着色的

但是本人实测9.3版本的lvgl不适用这种方法,所以只能设置不同状态下的不同图片了

就是内存占用多了很多

设置按钮的默认状态

lv_imagebutton_set_state(imgbtn_snow, LV_IMAGEBUTTON_STATE_RELEASED);

窗口部件(window)

新版本的lvgl不像旧版本那样在创建函数的时候就可以一步到位设置其部位的尺寸,

新版本需要分开获取其组成部分然后分别设置

header部分

lv_obj_t* window_header = lv_win_get_header(window);
lv_obj_set_size(window_header, 250, 25);

设置标题内容

lv_obj_t* window_title = lv_win_add_title(window, "Setting");

设置按钮

lv_obj_t* window_button = lv_win_add_button(window, LV_SYMBOL_CLOSE, 50);

需要注意的是,如果先设置标题再设置按钮,那么按钮就会向右边对齐

如果先设置按钮再设置标题,按钮会向左对齐

也就是说:title的设置下button设置的前面,所以button会进行右对齐,换句话说,在title前面设置的按钮会出现在title左边,并进行左对齐,在title后面设置的按钮会出现在title的右边,并进行右对齐

获取窗口的主体部分

lv_obj_t* winodw_content = lv_win_get_content(window);

需要在主体部分显示的界面均以该主体部件为父对象,例如:

lv_obj_t* window_label = lv_label_create(winodw_content);
lv_obj_t* window_label_music = lv_label_create(winodw_content);
lv_obj_t* window_label_sound = lv_label_create(winodw_content);

弹窗部件(messagebox)

消息框即消息弹窗,它可用于消息通知、内容提示、信息确认,等等。消息框部件可以设置为模态或非模态,当用户选择模态时,消息框弹出,仅有消息框的区域可点击,其他区域的点击无效。

添加关闭按钮

lv_obj_t* msgbox_close_btn = lv_msgbox_add_close_button(messagebox1);

需要注意的是,此函数会为删除按钮创建一个事件回调函数,如果按下此按键,弹窗部件就会被删除,源码如下:

lv_obj_t * lv_msgbox_add_close_button(lv_obj_t * obj)
{
    lv_obj_t * btn = lv_msgbox_add_header_button(obj, LV_SYMBOL_CLOSE);
    lv_obj_add_event_cb(btn, msgbox_close_click_event_cb, LV_EVENT_CLICKED, NULL);
    return btn;
}


static void msgbox_close_click_event_cb(lv_event_t * e)
{
    lv_obj_t * btn = lv_event_get_current_target(e);
    lv_obj_t * mbox = lv_obj_get_parent(lv_obj_get_parent(btn));
    lv_msgbox_close(mbox);
}


void lv_msgbox_close(lv_obj_t * obj)
{
    if(lv_obj_has_flag(obj, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_delete(lv_obj_get_parent(obj));
    else lv_obj_delete(obj);
}

如果之后尝试获取(例如再次显示弹窗),则会因访问非法区域而造成程序崩溃

添加底部按钮

lv_obj_t* msgbox_continue_btn;
lv_obj_t* msgbox_no_btn ;
msgbox_continue_btn = lv_msgbox_add_footer_button(messagebox1, "Continue");
msgbox_no_btn = lv_msgbox_add_footer_button(messagebox1, "No");

添加弹窗标题

lv_obj_t* msg_title = lv_msgbox_add_title(messagebox1, "Warning");

添加弹窗正文内容

lv_msgbox_add_text(messagebox1, "Do you want to continue?");

获取弹窗的标题框部分

lv_obj_t* msgbox_header = lv_msgbox_get_header(messagebox1);
lv_obj_set_size(msgbox_header, 250, 25);

特别注意

一般的弹窗,我们常见的效果是,弹窗弹出的时候,弹窗的显示部分,会覆盖之前的界面,此时用户需要优先处理弹窗的内容,才能回到之前的界面

所以这里就再次涉及了lvgl的层级:后创建的部分显示优于先创建的部分,所以实现弹窗的时候,弹窗部件一定要最后创建:

反之,当弹窗部件显示的时候,会发现之前的界面会混合显示:

部件的组成部分

有的时候,我们需要对部件的某个组成部分单独的设置其样式,所以我们需要实验获取某个组成部分的函数

对于这个获取某个组成部分的函数,需要注意,有的部件刚刚创建出来的时候(只调用了create函数),只会显示某些组成部分(算了语文不好,说不清,举个例子)

对于弹窗部件,有header部分,所以有函数lv_msgbox_get_header函数,是用来获取标题框的,但是当我们刚刚创建弹窗的时候,弹窗是没有header这个部分的

需要通过lv_obj_t* msg_title = lv_msgbox_add_title(messagebox1, "Warning");添加标题后,此时header部分才会存在

那么问题来了,如果你在lv_msgbox_add_title之前调用lv_msgbox_get_header,经小生实测,程序会因为访问野指针而导致程序崩溃

所以小生认为,以后获取部分内容的时候,如果出现程序崩溃,应该就是这里的问题了,特此记录

微调器部件(spinbox)

微调器部件本质上就是文本区域部件,它们的组成类似

设置范围

lv_spinbox_set_range(spinbox1, -9999, 9999);

设置步进值

lv_spinbox_set_step(spinbox1, 200);

设置当前数值

lv_spinbox_set_value(spinbox1, 0);

显示小数

lv_spinbox_set_digit_format(spinbox1, 4, 2);

参数的意思是,4位数字,小数部分占2位

特别注意

微调器显示小数,只是在GUI界面上显示小数,但是其数值并没有改变,读取出来的数值依然是整数,所以如果需要,就要对数值进行处理,需要进行转化,例如取整取余

设置光标位置

lv_spinbox_set_cursor_pos(spinbox1, 1);

对于第二个参数,0就是最低位,1是第二低位

注意

使用设置光标位置函数的时候,步进值设置会影响光标设置,也就是说,如果想要设置光标的位置,就不要使用步进值,让其默认为1即可,两者不可混用,只用其一

获取数值

lv_spinbox_get_value(spinbox1)

增加数值

lv_spinbox_increment(spinbox1);

减小数值

lv_spinbox_decrement(spinbox1);

表格部件(table)

表格部件是由一个个单元格组成的,该部件经过了专门的优化,它非常轻量化。
较为遗憾的是:表格部件的单元格中只能存放文本形式的内容,不支持存放其他任何类型的对象或者部件。

设置列数

lv_table_set_column_count(table1, 2);

设置行数

lv_table_set_row_count(table1, 3);

设置列宽

第二个参数位列的序号(从0开始)

for(unsigned char i = 0; i < 2; i++)
{
    lv_table_set_column_width(table1, i, 160);
}

设置列表内容

参数的意思是第3行第0列的数据(注意无论是列还是行都是从0开始的)

lv_table_set_cell_value(table1, 2, 0, "Banana");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值