页面数据刷新

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接口

接口名称

功能描述

lv_obj_data_subscribe

为UI控件订阅指定数据ID,绑定刷新回调函数

lv_obj_datasubs_notify

底层数据更新时,通知订阅该数据ID的控件触发刷新

  1. lv_obj_data_subscribe 接口参数说明

参数名

类型

功能描述

obj

LVGL 控件指针

需绑定数据的 UI 控件(如标签、进度条)

idx

无符号整数

数据唯一标识 ID(需与底层数据源 ID 一致)

cb

函数指针(lv_obj_datasubs_cb_t

数据更新时触发的回调函数,需在其中实现 UI 刷新逻辑

  1. lv_obj_datasubs_notify 接口参数说明

参数名

类型

描述说明

id

const char*

订阅标识字符串指针,需与订阅时使用的标识一致,用于匹配目标订阅者

type

uint32_t

通知消息类型,用于在回调函数中区分不同的通知场景(如 LV_EVENT_REFRESH

data

const void*

原始传输消息的指针,指向需要传递的最新数据

len

uint16_t

消息数据的长度(单位:字节),用于安全解析数据

user_data

void*

传递给回调函数的自定义用户数据,可用于传递上下文信息

返回值

int

错误编号(非负表示成功,负数表示失败)

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 层:

  1. 数据源适配层触发通知(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;
        }
    }
}
  1. 实现通知逻辑(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中。所以在唤醒的时候需要主动的刷新一下显示内容。