news 2026/10/5 4:35:41

LVGL学习笔记(八)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LVGL学习笔记(八)

LVGL学习笔记(八)

多个屏幕的切换&动画

前言

在前面的笔记中,我们了解了LVGL按键、标签等基础控件的使用,学习不同页面布局以及事件、定时器等LVGL核心功能。然而,在实际的LVGL开发中,项目中有很大的概率不只有一个界面。所以本篇笔记我们围绕学习多个界面的创建、切换、删除等基本功能展开,最后实现使用按键切换两个不同的界面。

  • 在LVGL中,如何实现多个界面的切换?一共有几种方法?

    在 LVGL 中实现多个界面切换,本质上是管理对象树的显示与隐藏。可以初步的分为如下的5种方法:

    1.屏幕全切换

    使用函数lv_scr_load() 可以将整个现象界面替换,达到屏幕切换的功能。其函数原型如下:

    staticinlinevoidlv_scr_load(lv_obj_t*scr){lv_disp_load_scr(scr);}voidlv_disp_load_scr(lv_obj_t*scr){lv_scr_load_anim(scr,LV_SCR_LOAD_ANIM_NONE,0,0,false);}

    其本质是调用了一个无动画的lv_scr_load_anim()函数,该函数的原型会在下面提到,这里不做展开。

    但需要注意的是,在LVGL中,由于lv_obj_create(NULL) 创建的Screen只是一个没有父对象的容器,而lv_scr_load()也只是把"当前激活指针"换掉,使刷新的界面发生变化。故在页面比较少的时候,使用这种方式切换界面不会出现大的问题,但在复杂的项目中,如超过3个以上的界面需要切换的情况下,必须要进行界面管理,而LVGL官方并没有提供专门的"页面管理器",故需要开发人员自己进行实现。该部分的内容后面的笔记中会具体展开,本篇笔记不展开讨论。

    2.屏幕动画切换

    使用函数lv_scr_load_anim()进行界面切换,其函数原型如下:

    voidlv_scr_load_anim(lv_obj_t*new_scr,lv_scr_load_anim_tanim_type,uint32_ttime,uint32_tdelay,bool auto_del)

    其参数内容说明如下:

    参数说明
    new_scr要切换到的目标屏幕对象
    anim_type动画类型枚举
    time动画持续时间,单位毫秒
    delay延迟多少毫秒后才开始动画
    auto_del动画结束后是否自动删除旧屏幕对象

    常用动画效果如下所示:

    枚举值效果
    LV_SCR_LOAD_ANIM_NONE无动画
    LV_SCR_LOAD_ANIM_MOVE_LEFT/RIGHT/TOP/BOTTOM新旧屏一起滑动
    LV_SCR_LOAD_ANIM_OVER_LEFT/RIGHT/TOP/BOTTOM新屏从边缘覆盖
    LV_SCR_LOAD_ANIM_FADE_ON淡入效果
    LV_SCR_LOAD_ANIM_OUT_LEFT旧屏滑出,新屏原地显现

    3.使用FLAG 控制对象隐藏/显示

    不切换 Screen,而是在同一个 Screen 内创建多个容器(页面),通过隐藏/显示标志位来切换。

    举例代码如下:

    // 创建两个界面(父对象为当前活动屏幕)lv_obj_t*Screen1=lv_obj_create(lv_scr_act());lv_obj_t*Screen2=lv_obj_create(lv_scr_act());// 切换:隐藏 page1,显示 page2lv_obj_add_flag(Screen1,LV_OBJ_FLAG_HIDDEN);// 隐藏lv_obj_clear_flag(Screen2,LV_OBJ_FLAG_HIDDEN);// 显示

    函数lv_obj_add_flag(),函数原型如下:

    voidlv_obj_add_flag(lv_obj_t*obj,lv_obj_flag_tf)

    其参数内容说明如下:

    参数说明
    obj目标对象
    f要添加的标志

    常用添加的标记如下所示:

    标志说明
    LV_OBJ_FLAG_CLICKABLE隐藏对象
    LV_OBJ_FLAG_HIDDEN使对象可点击
    LV_OBJ_FLAG_SCROLLABLE使对象可滚动
    LV_OBJ_FLAG_SCROLL_ON_FOCUS当对象获得焦点时滚动
    LV_OBJ_FLAG_EVENT_BUBBLE允许事件冒泡到父对象
    LV_OBJ_FLAG_GESTURE_BUBBLE允许手势事件冒泡到父对象
    LV_OBJ_FLAG_FLOATING使对象浮动(不占据布局空间)
    LV_OBJ_FLAG_IGNORE_LAYOUT忽略布局更新
    LV_OBJ_FLAG_ADV_HITTEST启用高级命中测试(允许自定义命中区域)

    函数lv_obj_clear_flag(),函数原型如下:

    voidlv_obj_clear_flag(lv_obj_t*obj,lv_obj_flag_tf)

    参数同函数lv_obj_add_flag(),都无返回值。

    4.更改父对象

    将页面从屏幕树上"摘下来"再"挂上去",实现按需加载。

    举例代码如下:

    // 切换时:将Screen1移出渲染树,将Screen2挂回屏幕lv_obj_set_parent(Screen1,NULL);// 脱离渲染树(不显示,不占用渲染资源)lv_obj_set_parent(Screen2,lv_scr_act());// 挂载到当前屏幕

    函数lv_obj_set_parent()的函数原型如下所示:

    voidlv_obj_set_parent(struct_lv_obj_t*obj,struct_lv_obj_t*parent);

    该函数在前面笔记讲解父对象时有过介绍,这里不再次展开阐述。

    5.使用选项卡视图

    LVGL 提供的内置组件,自带滑动切换和指示器。

    示例代码如下:

    lv_obj_t*tabview=lv_tabview_create(lv_scr_act(),LV_DIR_TOP,50);lv_obj_t*tab1=lv_tabview_add_tab(tabview,"设置");lv_obj_t*tab2=lv_tabview_add_tab(tabview,"系统");// 在各 tab 中添加内容即可lv_label_create(tab1);lv_label_create(tab2);

    Tabview(选项卡)对象可用于组织选项卡中的内容。选项卡对象是由其他控件组件而成的。

    简单来说,lv_tabview 是一个由顶部/侧边按钮栏和内容显示区组成的复合对象。

    按钮栏:包含多个标签按钮,点击可切换页面。
    内容显示区:显示当前激活。

    使用 lv_tabview 非常简单,只需要三步:创建控件 -> 添加标签 -> 添加内容。

    可以通过函数lv_tabview_create()来实现创建控件,其函数原型如下:

    lv_obj_t*lv_tabview_create(lv_obj_t*parent,lv_dir_ttab_pos,lv_coord_ttab_size)

    其参数内容说明如下:

    参数说明
    parent对象
    tab_pos按钮栏的位置
    tab_size按钮栏的高度

    返回值为指向新创建的 TabView 对象本身。

    可以通过函数lv_tabview_add_tab()来添加新选项卡,可以在其上创建选项卡的内容,其函数原型如下:

    lv_obj_t*lv_tabview_add_tab(lv_obj_t*obj,constchar*name)

    参数obj为其对象,name为标签名,返回值为指向可以创建选项卡内容的对象指针。

    使用函数lv_label_create()创建选项卡的内容,其函数原型如下:

    lv_obj_t*lv_label_create(lv_obj_t*parent)

    其参数为新标签对象,会挂到这颗对象子树下,位置、裁剪、滚动、显隐全部跟随父对象。返回值为新 Label 的对象指针。

  • 这几种方式有什么不同?都有什么特点?分别适合用在什么场景下?

    维度方法一 裸切方法二 动画方法三 FLAG方法四 摘挂方法五 TabView
    内存占用中中最大中中
    切换速度最快中等极快快中等
    动画效果❌✅ 丰富❌(可自制)❌(可自制)✅ 水平滑动
    状态保持✅✅(false时)✅✅✅
    公共元素复用❌❌✅ 最佳✅✅
    代码复杂度低低中高低
    适合层级导航✅✅✅✅❌
    适合平级导航❌❌✅❌✅ 最佳
    页面数量上限~10~10~5~15~5

    真实项目中很少只用一种方法,常见组合如下:

    组合模式说明
    主框架用 FLAG + 子页面用动画顶部标题栏/底部导航栏用 FLAG 常驻,点击导航后内容区用lv_scr_load_anim切换
    登录流程用裸切 + 主界面用动画登录→主页用lv_scr_load(单向、不需返回),主页内各功能页用lv_scr_load_anim(需要动画)
    TabView + FLAGTabView 管理顶层并列导航,每个 tab 内部有多个子页面用 FLAG 切换
  • LVGL中的动画系统是如何实现的?

    LVGL 的动画系统是一个纯软件、时间驱动、回调执行的调度器。

    它不依赖硬件加速,也不绑定帧率,而是靠lv_tick时间戳 + 内部定时器,周期性计算“当前时刻应该处于什么值”,再通过exec_cb把这个值写回对象。

    理解了这个模型,就能解释前面遇到的跳变、卡顿以及 函数lv_scr_load_anim()到底做了什么。

下面给出一个简单的界面切换示例,使用的是第二种方法,这是项目中最常用的方法,也是LVGL官方推荐的方法,其代码如下:

*====================全局变量:两个屏幕对象====================*/staticlv_obj_t*screen1=NULL;staticlv_obj_t*screen2=NULL;voidlv_mainstart(void){/* 1. 创建两个屏幕(不显示,仅在内存中准备) */create_screen1();create_screen2();/* 2. 默认加载 screen1 作为初始画面 */lv_scr_load(screen1);}/* ==================== 创建屏幕 1 ==================== */staticvoidcreate_screen1(void){/* 创建屏幕(父对象为 NULL,表示它是顶层屏幕) */screen1=lv_obj_create(NULL);lv_obj_set_style_bg_color(screen1,lv_color_hex(0x1B3A4B),0);// 深蓝背景/* 标题标签 */lv_obj_t*title=lv_label_create(screen1);lv_label_set_text(title,"Screen 1");lv_obj_set_style_text_color(title,lv_color_white(),0);lv_obj_align(title,LV_ALIGN_CENTER,0,-60);/* 创建切换按钮 */lv_obj_t*btn=lv_btn_create(screen1);lv_obj_set_size(btn,150,50);lv_obj_align(btn,LV_ALIGN_CENTER,0,20);/* 按钮文字 */lv_obj_t*btn_label=lv_label_create(btn);lv_label_set_text(btn_label,"Go to Screen 2");lv_obj_align(btn_label,LV_ALIGN_CENTER,0,0);/* 绑定点击事件 */lv_obj_add_event_cb(btn,btn_click_event_cb,LV_EVENT_CLICKED,screen2);}/* ==================== 创建屏幕 2 ==================== */staticvoidcreate_screen2(void){screen2=lv_obj_create(NULL);lv_obj_set_style_bg_color(screen2,lv_color_hex(0x4B1B3A),0);// 深红背景/* 标题标签 */lv_obj_t*title=lv_label_create(screen2);lv_label_set_text(title,"Screen 2");lv_obj_set_style_text_color(title,lv_color_white(),0);lv_obj_align(title,LV_ALIGN_CENTER,0,-60);/* 创建切换按钮 */lv_obj_t*btn=lv_btn_create(screen2);lv_obj_set_size(btn,150,50);lv_obj_align(btn,LV_ALIGN_CENTER,0,20);/* 按钮文字 */lv_obj_t*btn_label=lv_label_create(btn);lv_label_set_text(btn_label,"Back to Screen 1");lv_obj_align(btn_label,LV_ALIGN_CENTER,0,0);/* 绑定点击事件 */lv_obj_add_event_cb(btn,btn_click_event_cb,LV_EVENT_CLICKED,screen1);}/* ==================== 按键点击事件回调 ==================== */staticvoidbtn_click_event_cb(lv_event_t*e){/* 1. 获取当前正在显示的屏幕 */lv_obj_t*current_scr=lv_scr_act();lv_obj_t*target_scr=NULL;/* 2. 动态判断:当前是哪个屏幕,就切换到另一个 */if(current_scr==screen1){target_scr=screen2;}else{target_scr=screen1;}/* 动画参数: * LV_SCR_LOAD_ANIM_MOVE_RIGHT :从右向左滑入(新屏幕从右侧推入) * 500 : 动画持续时间 500ms * 0 : 无延迟 * false: 切换后不删除旧屏幕(保留以便下次切回来) */lv_scr_load_anim(target_scr,LV_SCR_LOAD_ANIM_MOVE_RIGHT,500,0,false);}

示例实验结果如下:

界面切换

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 4:35:29

光伏MPPT仿真:灰狼优化与扰动观察法混合策略解析

去年做离网光伏储能项目调试时,我踩过最折腾的一个坑:电池侧电压稳了,可光伏侧功率始终到不了铭牌值,一查发现是MPPT算法被多峰曲线困在了局部极值点。当时用的就是经典扰动观察法(P&O),单峰…

作者头像 李华
网站建设 2026/10/5 4:34:42

AI员工可观测性实战:基于执行网关的日志采集与重放体系

1. 为什么“能跑”的AI员工系统,最后都卡在了“说不清”上做AI员工系统的团队,几乎都会经历同一个阶段:Demo跑通那一刻,所有人都觉得这事成了。Agent能接需求、能调工具、能写文件、能发消息,流程串起来像模像样。可一…

作者头像 李华
网站建设 2026/10/5 4:34:41

YOLOv8+DeepSORT火情定位系统实战:亚秒级响应与厘米级定位

简介:本资源是一份面向计算机视觉与智能安防领域初学者及工程实践者的专业参考文献,聚焦火灾检测这一典型工业应用场景,解决传统接触式传感器在复杂环境下误报率高、响应滞后等痛点。文档基于OpenCV开源库,系统阐述红外基础理论、…

作者头像 李华
网站建设 2026/10/5 4:34:22

RBF神经网络监督控制在船舶自动舵中的原理与Python实现

简介:这份PDF文献聚焦RBF神经网络监督控制在船舶自动舵中的研究与应用,面向自动化、控制工程及船舶操纵方向的学习者与研究人员,帮助理解非线性航向控制中传统PID难以应对的难题。资源包共1个PDF文件,约215KB,内容完整…

作者头像 李华
网站建设 2026/10/5 4:33:52

STM32主从定时器实现伺服PULSE+DIR精确脉冲控制

1. 这不是普通PWM,是伺服系统里“数脉冲”的硬核控制逻辑你手上正调试一块STM32,驱动着一台工业级伺服电机,目标很明确:让电机精准转到某个机械角度,误差不能超过0.01度。这时候你发现,用常规的占空比调节P…

作者头像 李华
网站建设 2026/10/5 4:33:46

修复PyCharm调试asyncio的ProactorEventLoop报错

在 Windows 上调试 asyncio 项目时,刚把断点打在协程的await行上,PyCharm 没有停在预期位置,反而弹出一个让我一度很懵的报错:ProactorEventLoop object has no attribute _compute_internal_coro。代码本身跑起来完全正常&#x…

作者头像 李华