平铺应用¶
1. 简介¶
平铺应用是智能穿戴设备(如手表)中由主应用框架管理的核心交互界面集合,包含表盘、上下快捷页面及左右子应用,作为用户日常操作的主要入口,支持展示各类功能模块(如活动记录、心率监测等)并提供便捷的切换交互。
平铺功能通过
APP_TLV_USED使能,配置路径为:menuconfig (Top) → Gui Framwork Config → Tileview Config。使能平铺相关的宏之后,Solution 框架将提供自动注册以及调度机制
支持最少一个子应用,没有最大数量限制(视内存情况而定)
支持切换动画平移、缩放、弧形旋转,水平 3d 翻转、绕 Y 轴翻转,以及用户自定义动画
他的调度与主应用框架类似,通过 msg_handle 进行,msg 中的参数是该应用的节点信息,其结构是
tlv_node_t
注:有的用户也习惯将平铺应用称作一级页面
2. 注册平铺应用¶
注册平铺应用需使用TLV_REGISTER宏,语法如下:
TLV_REGISTER(priority, id, name, thumb_img, ptr_size)
参数说明:
priority:应用优先级,数值越小优先级越高,应用按优先级排序,支持动态调整(如编辑页面拖动排序)
id:应用标识字符串,框架调度时使用,需在平铺框架中保持唯一
name:应用显示标题,多语言字符串,仅用于显示
thumb_img:应用缩略图,用于编辑模式
ptr_size:应用全局内存大小,由框架统一申请和释放,页面可直接使用
3. 平铺应用实现¶
平铺应用的实现主要围绕生命周期函数和事件处理,核心包括以下几个部分:
3.1 生命周期函数¶
平铺应用有四个主要的生命周期函数,用于管理应用的创建、显示、隐藏和销毁:
on_start():应用初始化时调用,用于创建基本 UI 元素
on_resume():应用即将显示时调用,用于初始化动态元素和启动动画
on_pause():应用即将隐藏时调用,用于暂停动画和释放临时资源
on_stop():应用销毁时调用,用于释放所有资源
3.2 事件处理¶
应用需要处理各种用户交互事件和系统事件,如触摸、滑动、按键等,确保应用能够正确响应用户操作。
3.3 核心功能模块¶
平铺页面主要包含四部分核心功能:
TLV 应用管理
显示已设定的 TLV 应用和表盘
长按进入 TLV 编辑页面,支持 TLV 的添加、删除和排序操作
表盘管理
提供表盘的编辑与选择功能
支持将指定表盘设为 TLV 应用
包含六扇门表盘选择页面
辅助窗口
下拉消息框:显示通知和系统消息
上拉堆叠菜单:提供快捷功能入口
右滑侧边栏:展示附加信息或功能
快捷页面:快速访问常用功能
所有辅助框均通过lv_multegde控件封装了拖拽交互,简化开发
切换动画
TLV 滑动切换动画配置
上下拉页面切换动画管理
3.4 开发注意事项¶
帧率优化 平铺页面同时存在左、中、右三个子应用,为保证高刷新率,需注意:
页面设计尽量简洁,避免大长页
复杂页面考虑使用进场动画,延迟复杂元素初始化,既提高帧率又增加动感
禁止在平铺页面中进行 flash 读写和 delay 操作,历史数据应提前读取到内存
避免直接使用大尺寸的圆或弧,建议使用图片替代;若图片无法满足,可考虑使用绘制功能
状态机差异 平铺应用的状态机切换与普通应用不同,需特别注意:
普通应用:start->resume->pause->stop(顺序切换)
平铺应用可能的状态切换:
start->resume->pause->stop
start->pause->stop
start->stop
这种设计旨在提高状态切换效率,省去非必要过程,因此开发时需兼容以上所有场景。
3.5 示例代码¶
以下是一个活动记录应用的实现示例,使用进场动画来提高帧率:
static void on_start(void *param)
{
/* 获取该应用的parent obj */
lv_obj_t *parent = TLV_GET_NODE_PARENT;
/* 获取框架申请的全局内存 */
p_activity = TLV_GET_NODE_MEM_PTR;
LV_ASSERT_NULL(p_activity);
/* 创建背景图片 */
lv_obj_t *bg_img = lv_img_create(parent);
lv_img_set_src(bg_img, APP_GET_IMG(img_activity_bg_tlv));
lv_obj_center(bg_img);
lv_obj_add_flag(bg_img, LV_OBJ_FLAG_EVENT_BUBBLE);
/* 创建透明背景容器,其他子控件均创建在该容器上 */
lv_obj_t *bg_cont = lv_obj_create(bg_img);
lv_obj_remove_style_all(bg_cont);
lv_obj_set_size(bg_cont, 400, 210);
lv_obj_set_style_bg_color(bg_cont, LV_COLOR_YELLOW, LV_PART_MAIN);
lv_obj_set_style_bg_opa(bg_cont, 0, LV_PART_MAIN);
lv_obj_refr_size(bg_cont);
lv_obj_align(bg_cont, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_refr_pos(bg_cont);
lv_obj_add_flag(bg_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_clear_flag(bg_cont, LV_OBJ_FLAG_SCROLLABLE);
}
static void on_resume(void *param)
{
/* 初始化其他子控件 */
app_activity_init(0);
/* 启动进场动画,将arc页面初始化放在动画中,提高帧率并增加动感 */
lv_anim_t n_anim;
lv_anim_init(&n_anim);
lv_anim_set_values(&n_anim, 0, 1024);
lv_anim_set_time(&n_anim, 5000);
lv_anim_set_exec_cb(&n_anim, arc_anim_cb);
lv_anim_set_repeat_count(&n_anim, 500);
lv_anim_set_var(&n_anim, p_activity);
lv_anim_start(&n_anim);
}
static void on_pause(void *param)
{
/* 删除页面上的动画,防止页面停止时动画仍在运行引发异常 */
for (uint16_t i = 0; i < 3; i++)
{
lv_anim_del(&p_activity->arc_arr[i], NULL);
}
}
static void on_stop(void *param)
{
/* 指针变量置空 */
p_activity = NULL;
}
/* 注册活动记录的平铺应用 */
TLV_REGISTER(APP_PRIO_ACTIVITY, tlv_activity, app_get_strid(key_tlv_activity, "activity"),
img_activity_thum, sizeof(app_activity_t));
具体例程可以参见solution\examples\watch\application\tileview\tlv_sleep。
4. 平铺应用管理¶
4.1. 删除平铺应用¶
平铺应用节点结构定义如下:
typedef struct
{
tlv_desc_t desc; // TLV描述信息
lv_obj_t *scr; // 平铺应用父对象
void *mem_ptr; // 平铺应用全局内存指针
uint32_t mem_size; // 平铺应用全局内存大小
void *user_data; // 平铺应用用户数据
rt_list_t list; // 链表节点
tlv_state_t state; // 应用状态
bool installed; // 应用是否已安装,true表示已安装
bool updated; // 是否已更新,true表示已更新并将在关机时写入flash
} tlv_node_t;
删除方法: 将对应节点的installed属性设为false,系统在读取显示时会跳过该节点。 注意: 删除平铺应用时,该应用不能处于运行状态。
4.2. 编辑(排序)平铺应用¶
排序 TLV 应用的主要思路是调整 TLV 链表的节点顺序,以下是实现示例:
void tlv_edit_drage_item_cb(lv_event_t *e)
{
lv_obj_t *snapshot = lv_event_get_current_target(e);
lv_obj_t *multlist = lv_obj_get_parent(snapshot);
lv_event_code_t code = lv_event_get_code(e);
if (LV_EVENT_RELEASED == code)
{
rt_list_t *list = tlv_list_get();
if (NULL == item_ref)
{
// 将节点调整到链表最后面:移除节点后添加到最后
tlv_node_t *node = (tlv_node_t *)item->info;
rt_list_remove(&node->list);
rt_list_insert_before(list, &node->list);
}
else
{
// 将节点调整到参考节点前面:删除节点后插入到参考节点前
tlv_node_t *node = (tlv_node_t *)item->info;
tlv_node_t *node_ref = (tlv_node_t *)item_ref->info;
rt_list_remove(&node->list);
rt_list_insert_before(&node_ref->list, &node->list);
}
// 开启multlist调整动画
lv_multlist_item_move_before(multlist, item, item_ref, true);
item->major_pos = item->org_x;
int32_t target_x = lv_multlist_get_focus_pos(multlist, LV_MULTLIST_ALIGN_CENTER, item);
item->anim_pos_start = pos_x + snapshot->coords.x1 - (item->org_w >> 1);
lv_multlist_anim(item, 0, 1024, 300, wf_edit_recover_anim_exe, wf_edit_recover_anim_ready, NULL);
lv_multlist_anim(multlist, pos_x, target_x, 300, (lv_anim_exec_xcb_t)lv_multlist_set_pos, NULL, NULL);
edit->drage_item = NULL;
edit->focus_item = item;
}
// ...
}
排序功能通常在编辑模式中通过拖拽实现,提供直观的用户体验。
通过以上指南,开发者可以快速理解和实现平铺应用,为智能穿戴设备构建高效、美观的用户界面。