应用¶
1. 如何创建一个内置应用及子页面¶
内置应用通过注册宏定义在专属代码段,编译时整合到固件,系统启动时自动加载并管理生命周期,无需手动初始化。
1.1 注册一个内置应用¶
根据应用是否在主菜单显示,分为「普通应用」和「隐藏应用」,二选一即可。
1.注册一个普通应用
// 注册普通应用的宏,参数说明:
// key_str: 应用显示标题(多语言字符串),仅用于界面显示
// img: 应用缩略图资源(在主菜单中显示)
// app_name: 应用唯一标识字符串(框架内部调度使用,必须保证唯一性)
// ptr_size: 应用全局内存大小(由框架自动申请和释放,页面可直接使用该内存区域)
APPLICATION_REGISTER(key_str, img, app_name, ptr_size)
2.注册一个隐藏应用
主菜单添加一个应用时是判断是否有相应的应用图标,没有图标则不在主菜单显示
// 注册隐藏应用的宏(不在主菜单显示):
// key_str: 应用显示标题(多语言字符串)
// app_name: 应用唯一标识字符串
// ptr_size: 应用全局内存大小
APPLICATION_REGISTER_HIDDEN(key_str, app_name, ptr_size)
1.2 注册一个子页面¶
通过APP_PAGE_REGISTER接口可以为一个应用创建子页面。
// 注册子页面的宏:
// app: 所属应用的名称(必须与APPLICATION_REGISTER中注册的应用名一致)
// subpage: 子页面唯一标识字符串
// ptr_size: 子页面全局内存大小
APP_PAGE_REGISTER(app, subpage, ptr_size)
1.3 运行一个已注册的子页面¶
运行注册的子页面调用下面的接口。
// 运行指定应用的子页面:
// app_id: 应用名称(必须与注册时使用的应用名一致)
// sub_id: 子页面名称(必须与注册时使用的子页面名一致)
// user_data: 自定义数据指针(可通过APP_GET_PAGE_USERDATA_PTR接口获取,仅在msg_handle中有效)
gui_app_run_subpage(const char *app_id, const char *sub_id, void *user_data)
1.4 注册流程说明¶
为简化注册流程,框架消息注册以及入口注册均包含在应用注册接口以及页面注册接口内部,无需用户额外处理
APP_PAGE_REGISTER的逻辑
注册一个子页面
#define APP_PAGE_REGISTER(app, subpage, ptr_size) \
APP_MSG_HANDLER(on_start, on_resume, on_pause, on_stop); \
SECTION_ITEM_REGISTER(APP_SUBPAGE_SECTION_NAME, static const app_subpage_desc_t app_subpage) = \
{ \
.app_id = app, \
.page_id = subpage, \
.handler = msg_handler, \
.mem_size = ptr_size \
}
APPLICATION_REGISTER的逻辑
每个应用注册时会默认注册一个’root’的子页面,以保证每个应用至少有一个子页面(子页面运行层级与注册的先后顺序无关)
#define APPLICATION_REGISTER(key_str, img, app_name, ptr_size) \
APP_PAGE_REGISTER(app_name, "root", ptr_size); \
APPLICATION_MAIN(app_name, ptr_size); \
BUILTIN_APP_EXPORT(key_str, APP_GET_IMG(img), app_name, app_main, 1); \
BUILTIN_APP_EXPORT(key_str, APP_GET_IMG(CONCAT_2(img, 2)), app_name, app_main, 2);
#define APPLICATION_REGISTER_HIDDEN(key_str, app_name, ptr_size) \
APP_PAGE_REGISTER(app_name, "root", ptr_size); \
APPLICATION_MAIN(app_name, ptr_size) \
BUILTIN_APP_EXPORT(key_str, NULL, app_name, app_main, 1); \
BUILTIN_APP_EXPORT(key_str, NULL, app_name, app_main, 2);
1.5 示例¶
注册一个普通应用(闹钟应用)
// 页面启动回调函数:框架发送START消息时调用
static void on_start(void)
{
/* 获取框架申请的内存,全局指针所对应的结构体大小一定要和申请的大小一致 */
// APP_GET_PAGE_MEM_PTR是框架提供的接口,用于获取预先申请的内存指针
p_alarm = (app_alarm_gui_t *)APP_GET_PAGE_MEM_PTR;
RT_ASSERT(p_alarm);
/* 在当前screen上面建立背景页面,包括基础数据读取以及页面标题栏 */
p_alarm->bg_page = app_alarm_gui_init(lv_scr_act());
/* 创建一个刷新timer,定时查询状态,亦可使用数据订阅 */
p_alarm->timer = lv_timer_create(alarm_task_cb, 1000, (void *)0);
/* 将timer设置pause状态,因为初始页面不需要刷新,暂时关闭timer */
lv_timer_pause(p_alarm->timer);
}
// 页面激活回调函数:页面显示到前台时调用
static void on_resume(void)
{
/* 收到resume消息后,将该页面的所有内容创建完成 */
app_alarm_main_bg_page(p_alarm->bg_page);
/* 页面创建完成后启动刷新timer */
lv_timer_resume(p_alarm->timer);
}
// 页面暂停回调函数:页面被隐藏时调用
static void on_pause(void)
{
/* 页面进入隐藏不可见,关闭刷新timer */
lv_timer_pause(p_alarm->timer);
}
// 页面销毁回调函数:页面即将被销毁时调用
static void on_stop(void)
{
/* 页面即将销毁,先删除刷新timer,因为该timer不属于当前screen的子控件,销毁当前页面时不会自动销毁 */
lv_timer_del(p_alarm->timer);
/* 将全局指针变量置空,防止其他地方使用时非空判断出现异常,该指针指向的内存会在框架执行完stop消息后释放 */
p_alarm_main = NULL;
}
/* 注册Alarm应用,并使用框架申请的大小为'sizeof(app_alarm_gui_t)' 的内存 */
// 应用标题使用多语言字符串,图标使用img_alarm,应用名为"Alarm",内存大小为app_alarm_gui_t结构体大小
APPLICATION_REGISTER(app_get_strid(key_alarm, "Alarm"), img_alarm, "Alarm", sizeof(app_alarm_gui_t));
具体例程可以参见solution\examples\watch\application\setting\setting_main\setting_main_gui.c。
注册一个隐藏应用
具体例程可以参见solution\examples\watch\application\guide。
2. 如何创建一个外置应用/子页面/弹窗¶
外置应用也称动态应用或动态加载应用,可以通过外置应用功能实现应用市场。动态应用具体介绍
2.1 外置应用¶
2.1.1 注册一个外置应用¶
代码第一行定义DYN_APP宏,申明此APP是外置app,资源通过外置方式获取
// 定义此宏表明这是一个外置应用,资源将通过外置方式获取
#define DYN_APP
定义
on_start/on_resume/on_pause/on_stop调用宏注册应用 外置应用注册和内置应用注册一样,采用同一个宏APPLICATION_REGISTER。
APPLICATION_REGISTER(key_str, img, app_name, ptr_size)
2.1.2 示例¶
#define DYN_APP // 声明此为外置应用,资源通过外置方式获取
#include "global.h"
#include "dynamic_app.h"
// 定义模块名称(必须与动态应用APP_ID相同,模拟器调试使用)
#define _MODULE_NAME_ 'dyn_plane'
// 包含资源使用接口头文件
#include "app_module.h"
/**
* 用户逻辑代码
**/
// 页面生命周期回调函数
static void on_start(void)
{
/*user logic*/
}
static void on_resume(void)
{
/*user logic*/
}
static void on_pause(void)
{
/*user logic*/
}
static void on_stop(void)
{
/*user logic*/
}
APPLICATION_REGISTER(app_get_strid(key_plane, "Plane"), img_game_plane, "dyn_plane", sizeof(plane_play_t));
具体例程可以参见solution\examples\_dynamic_app\c\app\dyn_plane\src\game_plane_play.c。
2.2 外置的子页面¶
2.2.1 注册外置的子页面¶
代码第一行定义DYN_APP宏,申明此页面是外置页面,资源通过外置方式获取
// 定义此宏表明这是一个外置页面
#define DYN_APP
消息处理函数定义
// 注册页面消息处理回调函数
APP_MSG_HANDLER(on_start, on_resume, on_pause, on_stop)
外置应用的子页面典型使用方式为显示调用子页面调用函数,函数内部通过注册接口注册子页面,注册函数如下:
// 为外置应用创建子页面:
// app_id: 所属应用ID
// page_id: 子页面ID
// handler: 消息处理函数(固定为msg_handler)
// user_data: 自定义数据指针(可通过APP_GET_PAGE_MEM_PTR获取,仅在handler中有效)
// mem_size: 子页面全局内存大小
gui_app_create_page_for_app_ext(const char* app_id, const char* page_id, gui_app_msg_cb_t handler,
void* user_data, uint32_t mem_size)
2.2.2 示例¶
#define DYN_APP // // 声明此为外置页面,资源通过外置方式获取
#include "global.h"
#include "dynamic_app.h"
// 定义模块名称(必须与动态应用APP_ID相同,模拟器调试使用)
#define _MODULE_NAME_ 'dyn_plane'
// 包含资源使用接口头文件
#include "app_module.h"
// 定义子页面ID
#define PAGE_ID "setting"
/**
* 用户逻辑代码
**/
// 页面生命周期回调函数
static void on_start(void)
{
/*user logic*/
}
static void on_resume(void)
{
/*user logic*/
}
static void on_pause(void)
{
/*user logic*/
}
static void on_stop(void)
{
/*user logic*/
}
// 注册消息处理回调函数
APP_MSG_HANDLER(on_start, on_resume, on_pause, on_stop)
/*显示调用此函数创建子页面*/
int dyn_plane_subpage_create(void)
{
// 为外置应用"dyn_plane"创建子页面"setting"
// user_data和ptr_size根据实际情形填写(此处示例为NULL和0)
gui_app_create_page_for_app_ext("dyn_plane", PAGE_ID, msg_handler, NULL, 0)
}
具体例程可以参见solution\examples\_dynamic_app\c\app\activity\src\activity_target_gui.c。
2.3 外置弹窗¶
2.3.1 注册外置弹窗¶
1,代码第一行定义DYN_APP宏,申明此弹窗是外置弹窗,资源通过外置方式获取
// 定义此宏表明这是一个外置弹窗
#define DYN_APP
2,通过以下接口注册,目前一个app只能注册一个弹窗,如需要多弹窗功能,可在弹窗逻辑中区分,实现多弹窗功能
// 注册弹窗的宏:
// id: 弹窗名称(需要和应用ID一致)
// priority: 弹窗优先级(数字越大,优先级越高)
// time: 弹窗显示时间(生命周期,单位毫秒)
// ptr_size: 弹窗全局内存大小(可通过POPUP_GET_NODE_MEM_PTR使用)
POPUP_REGISTER(id, priority, time, ptr_size)
2.3.2 示例¶
// 声明此为外置弹窗
#define DYN_APP
#include "global.h"
#include "popup_fwk.h"
// 定义模块名称(必须与动态应用APP_ID相同,模拟器调试使用)
#define _MODULE_NAME_ 'dyn_plane'
// 包含资源使用接口头文件
#include "app_module.h"
/**
* 用户逻辑代码
**/
// 弹窗启动回调函数
static void on_start(void* param)
{
/*user logic*/
}
// 弹窗刷新回调函数
static void on_refr(void* param)
{
/*user logic*/
}
// 弹窗停止回调函数
static void on_stop(void* param)
{
/*user logic*/
}
// 注册弹窗:名称为"dyn_plane",优先级为5,显示时间为3000毫秒,内存大小为sizeof(ota_popup_t))
POPUP_REGISTER("dyn_plane", 5, 3000, sizeof(ota_popup_t)));
具体例程可以参见solution\examples\_dynamic_app\c\app\alarm\src\pop_alarm.c。
3. 如何调整开机后默认启动的应用¶
设置开机默认应用(设置后,会存入NVM)
void app_set_reg_power_on_app(const char *power_on_app)
获取开机默认应用
const char *app_get_reg_main_app(void)
4. 如何控制临时关闭动画换页效果?¶
当应用页面切换的动画效果(如 3D 翻页、滑动等)已开启时,默认会在页面跳转时展示动效。但部分场景下(例如连续启动两个页面,需跳过中间页面的过渡动画),可通过以下方式临时关闭动画:
操作方法
在调用页面创建接口(如
gui_app_create_page或gui_app_create_page_for_app)后,立即调用void gui_app_exec_now(void)函数,即可取消当前页面切换的动画过程。
实现原理
页面创建接口(
gui_app_create_page等)默认采用异步方式实现,通过send_msg_to_gui_app_task发送消息触发页面创建,因此会自然触发预设的切换动画gui_app_exec_now函数会强制同步执行页面创建流程,并在内部调用app_schedule_enable_trans_anim(false)临时关闭动画开关,从而跳过中间过渡效果
适用场景
多级页面连续跳转时,避免中间页面的过渡动画,提升操作流畅度
需要快速响应的交互场景,保留正常场景下的动画体验,又能在需要时避免多余动效
注意事项
此方法只会影响当前这次页面切换的动画效果,不会全局关闭动画
正常情况下的页面切换仍然会展示动画效果