707N UI调试指导
📎 原始文档:https://www.kdocs.cn/l/cpnLqID7s45v 707N UI调试指导
相关资料链接
- SDK文档获取

卡片页面
卡片页面
- 添加卡片 ui_page_list数组列举了要显示的卡片页面编号的宏,宏的值是ui_tools生成的页面路径crc值,如果修改了ui图片的路径需要在宏定义里重新修改宏。
如图所示在数组里添加要显示的页面的编号宏即可新增一个卡片。
如不能显示,检查以下函数
- 卡片切换效果 目前卡片切换有平移、淡入淡出、翻页的效果,这些都是使用基本的仿射变换实现的2d变换,可以自己再去实现别的效果。
- 添加卡片 ui_page_list数组列举了要显示的卡片页面编号的宏,宏的值是ui_tools生成的页面路径crc值,如果修改了ui图片的路径需要在宏定义里重新修改宏。
菜单页面
- 添加菜单 ui工程上的图标修改更换
菜单映射表,修改以下接口,可以把菜单图标列表的索引值映射到对应的跳转页面上
- 添加菜单 ui工程上的图标修改更换
const static MENU_MAP menu_map[] = {
{0, 0, ID_WINDOW_DIAL },
{1, 1, ID_WINDOW_PHONE },
{2, 2, ID_WINDOW_NOTICE },
{3, 3, ID_WINDOW_MUSIC_PLAYER },
{4, 4, ID_WINDOW_ALARM },
....
}
u32 ui_menu_map_by_sel(u32 sel, u32 menu_type);
2. 菜单效果
菜单页面处理主要在ui_sys_param_api.c文件内,从这里可以看到卡片移动的几个效果以及菜单页面的几个效果,根据这些效果可以进一步跟进到流程处理里
- 表盘页面
- 去掉侧边栏 sdk表盘右滑默认是侧边栏,关掉TCFG_UI_ENABLE_LEFT_MENU宏,去掉侧边栏
- 按键事件
- 按键使用一套简单驱动进行管理,调用结构比较麻烦,总的来说,从下到上分成以下个层次:按键值判断->按键消抖->按键集合收集->发送按键消息->按键事件处理。分成ad按键和io按键2种,ad按键就是用电阻值判断按键是否按下,io是用ttl电平判断。
- 我们要增加按键事件或者参考按键事件所调用的接口,可以看 ui_key_event_deal_app 函数
3D相关特效代码修改参数API
- 立方体模型 cube.h(卡片页面)
/*----------------------------------------------------------------------------*/
/**@brief 立方体模型初始化
@param width : 立方体每个面的宽度
@param heihgt : 立方体每个面的高度
@param w_scale : 立方体每个面宽度的缩放系数
@param h_scale : 立方体每个面高度的缩放系数
@param view_distance : 视距(值越小, 立方体越大)
@param win_x : win区域x坐标偏移
@param win_y : win区域y坐标偏移
@param win_w : win区域宽度
@param win_h : win区域高度
@note
*/
/*----------------------------------------------------------------------------*/
int cube_init(int width, int height, float w_scale, float h_scale, float view_distance, int win_x, int win_y, int win_w, int win_h);
/*----------------------------------------------------------------------------*/
- 足球模型football.h(菜单页面)
/*----------------------------------------------------------------------------*/
/**@brief 足球模型初始化
@param width : 足球球面宽度
@param height : 足球球面高度
@param scale : 足球球面缩放
@param view_distance : 视距(值越小, 足球越大)
@param win_x : win区域x坐标偏移
@param win_y : win区域y坐标偏移
@param win_w : win区域宽度
@param win_h : win区域高度
@note
*/
/*----------------------------------------------------------------------------*/
int football_init(int width, int height, float scale, float view_distance, int win_x, int win_y, int win_w, int win_h);
/*----------------------------------------------------------------------------*/
UI 操作示例
- 概述 该文档描述了一个用于耳机EQ(均衡器)控制的用户界面(UI)操作示例。该示例展示了如何通过触摸事件来切换EQ模式,并根据耳机的连接状态更新UI元素。
- 功能描述
- EQ模式切换:用户可以通过触摸“下一步”和“上一步”按钮来切换EQ模式。
- 连接状态指示:根据耳机的连接状态,UI会显示不同的图标(连接或断开)。
- 动态更新:在UI初始化和状态变化时,相关文本和图标会自动更新。
主要组件
3.1UI工程模式页面 添加页面并增加相关图片文字控件,命名好控件的名字

3.2宏定义 定义了一些常量和状态,用于控制UI行为。
#define LOG_TAG_CONST UI // 关闭当页的log_xx打印
#define CURR_PAGE PAGE_72// 添加页面
#define NEXT_BUTTON CS_IMG_EQ_NEXT// 下一步
#define LAST_BUTTON CS_IMG_EQ_PRE// 上一步
#define SMALLWIN CS_TEXT_EQ_MODE// EQ显示文本
- UI 事件注册 使用 REGISTER_UI_EVENT_HANDLER 宏注册UI事件处理程序,以便在特定控件上处理状态变化和触摸事件。
// EQ显示文本
REGISTER_UI_EVENT_HANDLER(SMALLWIN)
.onchange = my_onchange, //用于初始化状态和响应绘图事件
.ontouch = NULL;
// 下一步
REGISTER_UI_EVENT_HANDLER(NEXT_BUTTON)
. onchange = my_onchange, //用于初始化状态和响应绘图事件
.ontouch = my_ontouch; //触摸事件
// 上一步
REGISTER_UI_EVENT_HANDLER(LAST_BUTTON)
.onchange = my_onchange, //用于初始化状态和响应绘图事件
.ontouch = my_ontouch; //触摸事件
- 4.1状态枚举 定义了图标状态的枚举类型,用于表示耳机的连接状态。
typedef enum {
ICON_STATUS_DISCONNECT = 0,
ICON_STATUS_CONNECT,
ICON_STATUS_PRESSING,
} ICON_STATUS;
- 事件处理
- 5.1触摸事件处理 my_ontouch 函数处理触摸事件,包括按下和抬起事件。根据当前耳机状态,更新图标并切换EQ模式。
static int my_ontouch(void *_ctrl, struct element_touch_event *e)
{
struct ui_pic *pic = (struct ui_pic *)_ctrl;
switch (e->event) {
case ELM_EVENT_TOUCH_DOWN:
return true; // 控件接管消息
case ELM_EVENT_TOUCH_UP:
//过滤非点击动作
if(e->move_flag){
return false;
}
//未连接
if(!getbox()->earphone_state){
//发送弹窗提示并返回
ui_pic_show_image(pic, ICON_STATUS_DISCONNECT);
ui_server_msg_post("BarTop:Type=%4" , 5);
return false;
}
// 触发按钮点击动效
ui_pic_show_image(pic, ICON_STATUS_PRESSING);
os_time_dly(10);
ui_pic_show_image(pic, ICON_STATUS_CONNECT);
// 响应动作
struct element *elm = _ctrl;
switch(elm->id) {
case NEXT_BUTTON:
NOW_EQ_MODE = NOW_EQ_MODE>=4?0:NOW_EQ_MODE+1;
break;
case LAST_BUTTON:
NOW_EQ_MODE = NOW_EQ_MODE==0?4:NOW_EQ_MODE-1;
break;
}
g_printf("NOW_EQ_MODE %d", NOW_EQ_MODE);
// 发送EQ指令
custom_client_send_eq_mode(NOW_EQ_MODE);
// 更新EQ文字窗口
ui_text_show_index_by_id(SMALLWIN, NOW_EQ_MODE);
break;
default :
break;
}
return false;
}
- 5.2状态更新 EQ_UPDATA 和 BT_UPDATA 函数用于跨线程消息转发方式更新EQ模式和蓝牙连接状态的显示。
static int EQ_UPDATA(const char *type, u32 arg)
{
// 更新EQ模式
ui_text_show_index_by_id(SMALLWIN, NOW_EQ_MODE);
}
static int BT_UPDATA(const char *type, u32 arg)
{
// 更新按钮状态
ui_text_show_index_by_id(LAST_BUTTON, CONNECT_STATE?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
ui_text_show_index_by_id(NEXT_BUTTON, CONNECT_STATE?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
}
static const struct uimsg_handl ui_msg_handler[] = {
{ "EQ_UPDATA", EQ_UPDATA },
{ "BT_UPDATA", BT_UPDATA },
{ NULL, NULL}, /* 必须以此结尾! */
};
// 当前页面消息回调注册,CURR_PAGE是页面ID
case ON_CHANGE_INIT:
ui_register_msg_handler(CURR_PAGE, ui_msg_handler);
- 5.3定时器更新 update_timer 函数定期发送更新消息,以确保UI状态的实时性。
static void update_timer(void *p) {
UI_MSG_POST("EQ_UPDATA");
UI_MSG_POST("BT_UPDATA");
}
- 完整示例代码
/*
* @Author: lm
* @Date: 2024-07-20 15:23:11
* @LastEditors: lm
* @LastEditTime: 2024-07-20 15:23:11
* @Description: file content
* @FilePath: \sdk\apps\watch\ui\lcd\STYLE_SCREECBOX\screenbox_ui_noise.c
*/
#include "app_config.h"
#include "jlui_app/ui_style.h"
#include "jlui/ui.h"
#include "ui/ui_api.h"
#include "app_task.h"
#include "system/timer.h"
#include "device/device.h"
#include "key_event_deal.h"
#include "res/resfile.h"
#include "jlui_app/res_config.h"
#include "jlui_app/ui_resource.h"
#include "system/includes.h"
#include "audio_config.h"
#include "asm/mcpwm.h"
#include "jlui_app/ui_sys_param.h"
#include "jlui_app/watch_syscfg_manage.h"
#include "font/language_list.h"
#include "bt_common.h"
#include "btstack/btstack_task.h"
#include "btstack/avctp_user.h"
#include "custom_cfg.h"
// #include "product_info_api.h"
#include "cat1/cat1_common.h"
#include "screen_trans/smartbox_user_app.h"
#include "events_adapter.h"
#define LOG_TAG_CONST UI
#define LOG_TAG "[UI-ACTION]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".ui_eq.data.bss")
#pragma data_seg(".ui_eq.data")
#pragma const_seg(".ui_eq.text.const")
#pragma code_seg(".ui_eq.text")
#endif
#define STYLE_NAME JL
#if 1
typedef enum {
ICON_STATUS_DISCONNECT = 0,
ICON_STATUS_CONNECT,
ICON_STATUS_PRESSING,
} ICON_STATUS;
#define CURR_PAGE PAGE_72
#define NEXT_BUTTON CS_IMG_EQ_NEXT
#define LAST_BUTTON CS_IMG_EQ_PRE
#define SMALLWIN CS_TEXT_EQ_MODE
#define CONNECT_STATE (getbox()->earphone_state)
#define NOW_EQ_MODE (getbox()->eq_mode)
#define RLOAD 0
static int my_ontouch(void *_ctrl, struct element_touch_event *e)
{
struct ui_pic *pic = (struct ui_pic *)_ctrl;
switch (e->event) {
case ELM_EVENT_TOUCH_DOWN:
return true;
case ELM_EVENT_TOUCH_UP:
if(e->move_flag){
return false;
}
if(!getbox()->earphone_state){
ui_pic_show_image(pic, ICON_STATUS_DISCONNECT);
ui_server_msg_post("BarTop:Type=%4" , 5);
return false;
}
// 按钮动效
ui_pic_show_image(pic, ICON_STATUS_PRESSING);
os_time_dly(10);
ui_pic_show_image(pic, ICON_STATUS_CONNECT);
struct element *elm = _ctrl;
switch(elm->id) {
case NEXT_BUTTON:
NOW_EQ_MODE = NOW_EQ_MODE>=4?0:NOW_EQ_MODE+1;
break;
case LAST_BUTTON:
NOW_EQ_MODE = NOW_EQ_MODE==0?4:NOW_EQ_MODE-1;
break;
}
g_printf("NOW_EQ_MODE %d", NOW_EQ_MODE);
// 发送EQ指令。。
custom_client_send_eq_mode(NOW_EQ_MODE);
// 更新文字窗口
ui_text_show_index_by_id(SMALLWIN, NOW_EQ_MODE);
break;
default :
break;
}
return false;
}
static int EQ_UPDATA(const char *type, u32 arg)
{
ui_text_show_index_by_id(SMALLWIN, NOW_EQ_MODE);
}
static int BT_UPDATA(const char *type, u32 arg)
{
ui_text_show_index_by_id(LAST_BUTTON, CONNECT_STATE?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
ui_text_show_index_by_id(NEXT_BUTTON, CONNECT_STATE?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
}
static const struct uimsg_handl ui_msg_handler[] = {
{ "EQ_UPDATA", EQ_UPDATA },
{ "BT_UPDATA", BT_UPDATA },
{ NULL, NULL}, /* 必须以此结尾! */
};
static void update_timer(void *p){
UI_MSG_POST("EQ_UPDATA");
UI_MSG_POST("BT_UPDATA");
}
static int my_onchange(void *_ctrl, enum element_change_event event, void *arg)
{
struct element *elm = (struct element *)_ctrl;
struct ui_text *text = elm;
struct ui_pic *pic = elm;
struct draw_context *dc = (struct draw_context *)arg;
static int id = 0;
switch (event) {
case ON_CHANGE_INIT:
ui_register_msg_handler(CURR_PAGE, ui_msg_handler);
// 各个控件初始化
switch(elm->id) {
case SMALLWIN:
ui_text_set_index(text, NOW_EQ_MODE);
break;
case NEXT_BUTTON:
ui_pic_set_image_index(pic, (CONNECT_STATE==1)?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
break;
case LAST_BUTTON:
ui_pic_set_image_index(pic, (CONNECT_STATE==1)?ICON_STATUS_CONNECT:ICON_STATUS_DISCONNECT);
break;
}
if(!id) id = sys_timer_add(NULL, update_timer, 500);
break;
case ON_CHANGE_RELEASE:
if(id){
sys_timer_del(id);
id = 0;
}
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(SMALLWIN)
.onchange = my_onchange,
.onkey = NULL,
.ontouch = NULL,
};
REGISTER_UI_EVENT_HANDLER(NEXT_BUTTON)
.onchange = my_onchange,
.onkey = NULL,
.ontouch = my_ontouch,
};
REGISTER_UI_EVENT_HANDLER(LAST_BUTTON)
.onchange = my_onchange,
.onkey = NULL,
.ontouch = my_ontouch,
};
#else
void noise_ctrl_anc_off( u8 anc_mode)
{
}
#endif
- 结论 该UI操作示例展示了如何通过触摸事件实现耳机EQ模式的切换,并根据耳机的连接状态动态更新UI元素。通过合理的事件处理和状态管理,用户可以获得流畅的操作体验。
简单表盘进入缩放动效示例
在static int WATCH_onchange(void *ctr, enum element_change_event e, void *arg)添加以下函数,在第一次进入页面时动画与ID绑定,并触发动画效果
case ON_CHANGE_FIRST_SHOW:
// 卡片过渡时返回
if(ui_card_get_status()) break;
// 获取表盘页面ID
struct element * curr_elm = ui_core_get_element_by_id(DIAL_PAGE_0);
r_printf("%s %d %d %d ", __func__, __LINE__, curr_elm->css.left, ui_card_get_status());
// 缩放初始与结束值,归一化256
int start_pos = 512;
int end_pos = 256;
// 创建动画句柄
ui_anim_t a={0};
ui_anim_init(&a);
// 绑定ID与动画
ui_anim_set_var(&a, elm->parent->id);
// 选择贝塞曲线
ui_anim_set_path_cb(&a, ui_anim_path_ease_out);
// 动作回调
ui_anim_set_exec_cb(&a, watch_enter_anim_cb2);
// 设置动画起始结束值
ui_anim_set_values(&a, start_pos, end_pos);
// 整个动画的持续时间
ui_anim_set_time(&a, 80);
// 启动动画
ui_anim_start(&a);
break;
并在动画回调中处理更改控件的动作
static void watch_enter_anim_cb2(int var, int v)
{
// ID获取ELM
struct element *elm = ui_core_get_element_by_id(var);
if (!elm) {
return;
}
// 设置控件运动属性
if(v == 256){
ui_core_set_element_ratio_en(elm, 0);
} else {
ui_core_set_element_ratio(elm, (float)v/256.0, (float)v/256.0, 1);
}
// 绘制
ui_core_redraw(elm);
}