应用¶
FAQ1 如何新增一个应用(内置、外置)¶
应用由代码文件和资源文件(包含图片、动画、多语言配置文件等)两部分组成。 新增内置/外置应用的完整流程,可参考文档:创建一个新应用
FAQ2 如何基于已有应用修改为自定义应用¶
1. 修改内置应用¶
修改内置应用需分别处理代码文件和资源文件,具体步骤如下:
1.1 代码修改¶
完整复制已有应用的整个目录,将目录名、所有相关文件名统一修改为自定义名称(建议遵循项目命名规范,如小写字母+下划线的命名格式)。

清理冗余文件:删除与自定义应用功能无关的代码文件、配置文件。
处理函数关联依赖:逐一检查并调整代码中对原有目录、文件名、函数名的引用,确保所有关联逻辑指向修改后的内容,避免出现调用异常。
1.2 资源文件替换与适配¶
复制已有应用的资源目录,删除其中不需要的冗余资源(如多余的图片、动画文件)。

将自定义的新资源按原有目录结构放置到对应位置。
分辨率适配处理:参照新增应用分辨率支持文档进行适配,同时同步更新软件代码中对该资源名称的引用,确保资源能正常加载。
2. 修改外置应用¶
外置应用的代码与资源文件存放在同一目录下,修改流程更简化:
选择一个现有外置应用,复制其整个目录并修改为自定义名称。

其余修改步骤,参照外置应用创建流程执行即可。
FAQ3 如何删除一个已有的应用¶
1. 删除内置应用¶
删除内置应用需分代码删除和资源删除两个独立步骤,具体操作如下:
1.1. 删除代码¶
代码存放路径:
solution/examples/xxx/application/(其中xxx为具体产品型号,如watch、gridview等)。操作方法:直接删除对应应用的整个目录即可。
示例:删除
guide应用,可直接删除路径:solution\examples\watch\application\guide。
1.2. 删除资源¶
内置应用资源存放路径:
solution/examples/xxx/resource/。操作方法:仅需删除
images目录下对应应用的子目录即可;公共资源以及多语言表无需处理(原因:未使用的资源在编译时不会被链接,不影响项目体积;多语言表是从代码中检索生成的,会自动更新)。
2. 删除外置应用¶
2.1. 静态删除(项目编译前)¶
外置应用存放路径:
solution/examples/_dynamic_app/application/。应用类型与删除规则:该目录下包含C应用、Python应用、QJS应用、Tool应用等类型的外置应用,动态应用的代码与资源文件均存放在同一目录中。
操作方法:根据要删除的应用类型,进入对应子目录,直接删除整个目录。
示例:删除C应用下的
alarm应用,删除路径:solution/examples/_dynamic_app/application/c/app/alarm。
2.2. 动态删除(程序运行时)¶
外置应用支持程序运行后动态删除,具体操作参照:删除外置应用
FAQ4 如何添加全局自定义动画¶
1 新增动画类型¶
动画类型由主ID和次ID组成,主ID需唯一标识该动画,且不能与现有的重复。主ID范围以 sdk/middleware/lvgl/lvsf/lvsf_switchanim.h 中的内置枚举为准:
LV_SWITCHANIM_NONE 为无动画;
LV_SWITCHANIM_PUSH 至 LV_SWITCHANIM_SHUTTER 为系统内置动画;
LV_SWITCHANIM_DEFAULT 固定为 0xFF。
当前源码未定义客户自定义动画起始枚举,客户自定义动画请避开已有内置主ID,并使用小于 LV_SWITCHANIM_DEFAULT 的空闲值(例如从 0xEF 附近规划)。
2 注册动画回调¶
动画注册主要通过宏BUILTIN_ANIMATION注册,该宏定义如下:
#define BUILTIN_ANIMATION(anim_name,anim_major, anim_progress_cb) anim_name : 动画名称 anim_major: 动画主ID anim_progress_cb :动画回调函数
下面以缩放动画为案例说明该流程
static void lv_zoomanim_progress(lv_baseanim_t *baseanim, lv_obj_t *anim_obj, int32_t progress)
{
//缩放动画参数获取
lv_baseanim_para_t *para = lv_baseanim_get_para(baseanim);
uint16_t minor = para->minor;
lv_coord_t zoom_start = APP_TRANS_ANIM_ZOOM_NONE;
lv_coord_t zoom_end = 0;
lv_coord_t opa_start = LV_OPA_COVER;
lv_coord_t opa_end = LV_OPA_30;
//根据设备号区分不同的子动画
if (LV_ZOOMANIM_LARGER == para->minor)
{
zoom_start = APP_TRANS_ANIM_ZOOM_NONE;
zoom_end = APP_TRANS_ANIM_ZOOM_NONE << 2;
opa_start = LV_OPA_COVER;
opa_end = LV_OPA_30;
}
//动画主要分为进场动画和出场动画,每种方式单独处理
if (LV_BASEANIM_ENTER_TYPE == lv_baseanim_get_type(baseanim))
{
LV_COORD_SWAP(zoom_start, zoom_end);
LV_COORD_SWAP(opa_start, opa_end);
}
//缩放具体执行函数
lv_coord_t zoom = lv_map(progress, 0, LV_SWITCHANIM_PROGRESS_MAX, zoom_start, zoom_end);
lv_img_set_zoom(anim_obj, zoom);
lv_coord_t opa = lv_map(progress, 0, LV_SWITCHANIM_PROGRESS_MAX, opa_start, opa_end);
lv_obj_set_style_img_opa(anim_obj, opa, 0);
lv_obj_invalidate(lv_scr_act());
}
//具体动画注册,包含动画主ID号以及回调处理函数
BUILTIN_ANIMATION(zoomanim, LV_SWITCHANIM_ZOOM, lv_zoomanim_progress);
FAQ5 如何添加指定页面动画¶
1 调用框架接口指定动画类型¶
指定页面的切换动画主要设置进场动画类型和出场动画类型,分别调用以下接口
void gui_app_set_enter_anim_type(uint16_t major, uint16_t minor, int16_t minor_aux)
//major:动画主ID号
//minor:动画次ID号
//minor_aux:与之匹配另一个出场页面的动画次ID
void gui_app_set_exit_anim_type(uint16_t major, uint16_t minor, int16_t minor_aux)
//major:动画主ID号
//minor:动画次ID号
//minor_aux:与之匹配另一个进场页面的动画次ID
2 配置合适的动画优先级¶
页面切换过程中,如A页面切到B页面。动画框架会先比较两个页面所配置动画的优先级,**最终会使用优先级高的动画类型**,如果两个优先级相等,则使用上一级页面的动画类型
void gui_app_set_anim_prior(int16_t enter_prior, int16_t exit_prior)
enter_prior: 进场动画优先级
exit_prior: 出场动画优先级
FAQ6 如何关闭进场动画¶
1 调用框架接口关闭动画¶
关闭切换动画直接在框架接口函数on_start中调用void gui_app_close_anim()函数,该函数主要将进出动画类型设置为LV_SWITCHANIM_NONE,且优先级降为最高。
FAQ7 如何设置开机默认 APP 或 Home 键切换的 APP?¶
如果只是产品默认入口变化,不需要重新设计应用框架,可以按下面方式处理:
开机默认启动 APP:调用
app_set_reg_power_on_app设置目标 APP 名称;如果未设置,默认进入Main;Home 键主 APP:调用
app_set_reg_main_app设置主菜单类 APP,通常保持为Main;Home 键平铺/次 APP:调用
app_set_reg_tlv_app设置 Home 键循环切换的另一个 APP;也可以在设备界面通过
主菜单 -> 设置 -> 默认APP修改开机 APP 或平铺 APP。
接口说明和 UI 设置入口请参考 设置默认APP。
FAQ8 系统是如何判断是否息屏的?¶
系统通过 app_screen_lock_time_is_end() 进行判定,只有返回 true 时才会执行息屏。具体逻辑如下:
处于monkey测试(
monkey_mode()),则不息屏。锁屏开关(
screen_lock.enable,可通过app_screen_lock_enable控制)未打开,则不息屏。当设定阈值(
screen_lock.idle_time_limit,可通过app_screen_lock_time_set或app_screen_lock_time_temp_set修改)小于或等于当前实际空闲时间(lv_disp_get_inactive_time(NULL))时触发灭屏。
FAQ9 不息屏可能是什么原因?¶
参考 FAQ8, 常见原因如下:
通过
app_screen_lock_enable关闭了息屏。app_screen_lock_time_set或app_screen_lock_time_temp_set更改了灭屏阈值。lv_disp_trig_activity(NULL)刷新了当前空闲时间,常见有:UI 唤醒操作(
app_gui_wakeup/send_msg_to_gui_thread_e(msg, NEED_WAKEUP_UI))。按键/触摸操作。
可配合log具体确认。
FAQ10 lvgl的接口可以跨线程使用吗?¶
不行,由于lvgl是单线程,没有线程保护,建议所有ui处理都用send_msg_to_gui_thread接口发到gui线程统一处理,不然可能出现显示异常或者死机
FAQ11 快速连续创建page导致后续ui跳转异常¶
检查app_schedule_get_this是否返回null,如果返回的是null,则可能是gui_app_create_page_for_app_ext函数的变量page4app_msg_t初始化为0导致,可以修改为1解决
FAQ12: 如何修改root app id,即最终返回的app id¶
默认代码设置的是主菜单Main为root app,可以通过该函数app_schedule_change_main_app_id修改
FAQ13: 保活app的个数怎么设置¶
默认代码设置的是2个,可以通过app_schedule_max_running_apps函数修改
FAQ14: APP注册后不想在菜单里面显示,如何设置¶
可以使用APPLICATION_REGISTER_HIDDEN宏注册,用该图片注册的app没有带图标资源,所以不会在菜单里面显示
FAQ15: APP想注册不同的图标风格,如何设置¶
默认app注册接口APPLICATION_REGISTER会注册两个风格的图片,在资源mainmenu和mainmenu2目录添加对应的图标资源即可
FAQ16: APP进入后的第一个界面id是什么¶
APP注册接口有用到APP_PAGE_REGISTER(app_name, “root”, ptr_size);所以第一个界面id是"root";
FAQ17: 调用gui_app_xx_now后出现Recursive断言¶
该断言是gui_app_xx_now等接口调用的地方,本身就在app_schedule() 里面,所以出现递归了,
常见得情况是在msg_handle函数里面调用此类接口,要避免在msg_handle调用该接口
FAQ18: 调用gui_app_create_page后出现Create page %s[%x] error, invalid app handler断言¶
该断言是检测到调用该接口的时候没有active的app出现的断言,所以需要保证当前有active的app
FAQ19: 界面销毁后需要在on_stop手动删除obj吗?¶
如果是创建在lc_scr_act上的,on_stop阶段框架app schedule会自动删除obj,无需手动删除,其他例如top和sys层的obj需要手动删除