页面数据刷新¶
1. 介绍¶
思澈 Solution 为智能设备 UI 开发提供了完善的页面数据刷新机制,刷新采用主动定时轮询与被动数据订阅相结合的设计方案。两种机制均基于 LVGL 框架与平台实时数据库(app_db)实现,无需用户关注底层数据传输与线程调度细节,可根据数据更新特性(如固定频率、事件触发)灵活选型,快速实现页面内容的实时、高效更新,大幅降低开发复杂度。
2. 数据刷新机制¶
根据触发方式不同,数据刷新分为主动刷新与被动刷新两种模式,可根据场景需求灵活选择。
2.1 主动刷新:定时轮询更新¶
2.1.1 实现原理¶
主动刷新基于 LVGL 的定时器(lv_timer)实现,通过定时轮询数据源获取最新数据并更新 UI,适用于数据更新频率固定、需持续展示的场景(如步数、时间)。
2.1.2 示例¶
创建一个文本显示步数值,通过timer定时一秒更新一次显示内容
typedef struct
{
lv_obj_t *label;
lv_timer_t *refr_timer;
} app_step_t;
// 全局控件指针
app_step_t *p_step = NULL;
/**
* 定时器回调函数:刷新步数显示
*/
static void refresh_timer_cb(lv_timer_t *timer)
{
step_info_t *step_info = (step_info_t *) app_rt_info_get(RT_STEP);
lv_label_set_text_fmt(p_step->label, "%d", step_info->step);
}
/**
* 页面启动时初始化
*/
static void on_start(void)
{
p_step = app_malloc(sizeof(*p_step);
RT_ASSERT(step);
/*创建一个文本控件*/
p_step->label = lv_label_create(lv_scr_act());
/*设置文本的字号大小和字体颜色*/
lv_ext_set_local_font(p_step->label, FONT_SMALL, lv_color_make(0xBE, 0xBE, 0xBE));
/*创建一个timer,每秒更新一次显示内容*/
p_step->refr_timer = lv_timer_create(refresh_timer_cb, 1000, NULL);
}
/**
* 页面恢复时主动更新
*/
static void on_resume(void)
{
/*主动更新当前显示的步数值*/
step_info_t *step_info = (step_info_t *) app_rt_info_get(RT_STEP);
lv_label_set_text_fmt(p_step->label, "%d", step_info->step);
}
static void on_pause(void)
{
}
/**
* 删除定时器
*/
static void on_stop(void)
{
lv_timer_del(p_step->refr_timer);
app_free(p_step);
p_step = NULL;
}
2.2 被动刷新:数据订阅触发¶
被动刷新基于思澈平台的数据订阅机制,通过给UI控件绑定数据ID,当底层数据更新时主动通知UI触发刷新,适用于数据更新频率不固定、实时性要求高的场景(如心率、消息提醒)。
2.2.1接口¶
接口名称 |
功能描述 |
|---|---|
|
为UI控件订阅指定数据ID,绑定刷新回调函数 |
|
底层数据更新时,通知订阅该数据ID的控件触发刷新 |
lv_obj_data_subscribe接口参数说明
参数名 |
类型 |
功能描述 |
|---|---|---|
|
LVGL 控件指针 |
需绑定数据的 UI 控件(如标签、进度条) |
|
无符号整数 |
数据唯一标识 ID(需与底层数据源 ID 一致) |
|
函数指针( |
数据更新时触发的回调函数,需在其中实现 UI 刷新逻辑 |
lv_obj_datasubs_notify接口参数说明
参数名 |
类型 |
描述说明 |
|---|---|---|
|
|
订阅标识字符串指针,需与订阅时使用的标识一致,用于匹配目标订阅者 |
|
|
通知消息类型,用于在回调函数中区分不同的通知场景(如 |
|
|
原始传输消息的指针,指向需要传递的最新数据 |
|
|
消息数据的长度(单位:字节),用于安全解析数据 |
|
|
传递给回调函数的自定义用户数据,可用于传递上下文信息 |
返回值 |
|
错误编号(非负表示成功,负数表示失败) |
2.2.2 完整实现流程¶
被动刷新需完成UI 订阅配置与底层数据通知两部分开发:
步骤 1:UI 层配置数据订阅¶
// 全局控件指针
lv_obj_t *step_label = NULL;
/**
* 数据更新回调函数:被动刷新步数显示
*/
static void step_refresh_subs_cb(lv_obj_t *obj, lv_obj_datasubs_t *data)
{
// 从实时数据库获取最新数据
step_info_t *step_data = (step_info_t *)app_db_get_rt_data(RT_STEP);
if (step_data == NULL) return;
// 更新控件显示(obj为订阅时绑定的label控件)
lv_label_set_text_fmt(obj, "步数:%d", step_data->step);
}
/**
* 页面启动时初始化订阅
*/
static void on_start(void)
{
// 1. 创建步数显示标签
step_label = lv_label_create(lv_scr_act());
lv_ext_set_local_font(step_label, FONT_SMALL, lv_color_make(0xBE, 0xBE, 0xBE));
lv_obj_align(step_label, LV_ALIGN_CENTER, 0, 0);
// 2. 订阅步数数据(ID:SENSOR_APP_RT_STEP)
lv_obj_data_subscribe(step_label, SENSOR_APP_RT_STEP, step_refresh_subs_cb);
}
static void on_pause(void)
{
}
/**
* 页面恢复时主动更新(确保初始显示最新数据)
*/
static void on_resume(void)
{
step_info_t *step_data = (step_info_t *)app_rt_info_get(RT_STEP);
if (step_data != NULL) {
lv_label_set_text_fmt(step_label, "步数:%d", step_data->step);
}
}
static void on_stop(void)
{
}
步骤 2:底层配置数据通知¶
当传感器数据更新时,需通过lv_obj_datasubs_notify通知 UI 层:
数据源适配层触发通知(sensor_app_adaptor.c):
void sensor_app_lv_customize_process(hl_if_data_t *msg)
{
switch ((uint16_t)msg->sub_type)
{
// 步数数据更新事件
case SENSOR_APP_RT_STEP_INFO_IND:
{
#ifdef APP_DATA_NOTIFY
// 触发步数数据通知
sensor_rt_data_step_notify(msg);
#endif
// 将最新数据存入实时数据库
app_db_set_rt_data(RT_STEP, (void *)msg->data, msg->data_len);
break;
}
}
}
实现通知逻辑(app_data_notify.c):
/**
* 步数数据更新通知
*/
void sensor_rt_data_step_notify(hl_if_data_t *msg)
{
step_info_t *step_data = (step_info_t *)msg->data;
// 通知订阅SENSOR_APP_RT_STEP的控件刷新
lv_obj_datasubs_notify(
SENSOR_APP_RT_STEP, // 订阅的数据ID
LV_EVENT_REFRESH, // 事件类型(刷新)
&step_data->step, // 最新数据
sizeof(uint32_t) // 数据长度
);
}
3. 刷新场景¶
3.1 唤醒状态数据如何刷新¶
当系统处于唤醒状态,主动刷新和被动刷新均可使用。
3.2 睡眠状态数据如何刷新¶
当系统处于睡眠状态,此时UI线程已经停止工作,主动刷新的timer已经停止运行, 被动刷新的数据订阅机制也停止运行了。但是sensor数据的更新不受影响,仍然会将更新的值保存到app db中。所以在唤醒的时候需要主动的刷新一下显示内容。