UI入门简单实列
📎 原始文档:https://www.kdocs.cn/l/clG1N5VuQ58s UI入门简单实列
示例将展现的内容包括:列表的创建与使用、多张图片切换显示、文字控件strpic和text格式的显示和使用、数字控件string格式和numb格式的使用、不同方式的页面切换和数据源的使用。
//页面id管理
#define USR_TE_GRID_PAGE PAGE_39 //测试列表页面
#define USR_TE_PIC_PAGE PAGE_40 //测试图片页面
#define USR_TE_TEXT_PAGE PAGE_41 //测试文本页面
#define USR_TE_NUMB_PAGE PAGE_42 //测试数字页面
//垂直列表选中的布局id
#define USR_TE_SWITCH_PIC TE_SWITCH_PIC_LAYOUT
#define USR_TE_SWITCH_TEXT TE_SWITCH_TEXT_LAYOUT
#define USR_TE_SWITCH_NUMB TE_SWITCH_NUMB_LAYOUT
//控件id管理
#define USR_TE_VERTLIST TE_VERTLIST //垂直列表id
#define USR_TE_PIC TE_PIC //图片控件id
#define USR_TE_TEXT_NAME TE_TEXT_NAME //文本控件蓝牙名id
#define USR_TE_TEXT_ADDR TE_TEXT_ADDR //文本控件蓝牙地址id
#define USR_TE_TEXT_VER TE_TEXT_VER //文本控件版本号id
#define USR_TE_NUMB_NUM TE_NUMB_NUM //数字控件TYPE_NUM格式
#define USR_TE_NUMB_STR TE_NUMB_STR //数字控件TYPE_STRING格式
1.新建列表控件
首先每新建一个页面后需要添加图层和布局(带背景颜色),因为所有控件都需要在布局中使用。列表控件有垂直列表和水平列表,列表是带有滑动效果的,所以不需要编写滑动逻辑。但客户软件可以配置滑动方向。添加列表后如下图一样右键添加行、设置行高和间隔即可。设置好列表格式后会得到与添加行对应数量布局,只需要像下图一样添加想要显示的内容即可。下面通过添加strpic格式的文字控件作为示例,实现点击不同的项会进入不同界面的功能

typedef struct usr_menu_callback {
int sel_id;
int (*exit_check)();
int window;
int (*enter_check)(int window);
} USR_MENU_CB;
const static USR_MENU_CB usr_menu_cb[] = {
//{被选中的id, 退出检查, ,进入的页面id, ,进入检查},
{USR_TE_SWITCH_PIC, NULL, USR_TE_PIC_PAGE, NULL},
{USR_TE_SWITCH_TEXT, NULL, USR_TE_TEXT_PAGE, NULL},
{USR_TE_SWITCH_NUMB, NULL, USR_TE_NUMB_PAGE, NULL},
};
//此处初始化的时候只需要限制一下滑动的方向
static int usr_test_grid_onchane(void *ctr, enum element_change_event e, void *arg)
{
struct ui_grid *grid = (struct ui_grid *)ctr;
switch (e) {
case ON_CHANGE_INIT:
ui_grid_set_slide_direction(grid, SCROLL_DIRECTION_UD);
break;
case ON_CHANGE_RELEASE:
break;
case ON_CHANGE_FIRST_SHOW:
break;
default:
return false;
}
return false;
}
//选择处理
static int usr_menu_in_sel(struct ui_grid *grid)
{
u32 sel_item = ui_grid_cur_item(grid);
if (sel_item >= grid->avail_item_num) {
return false;
}
int id = grid->item[sel_item].elm.id;
for (int i = 0; i < (sizeof(usr_menu_cb) / sizeof(usr_menu_cb[0])); i++) {
if (usr_menu_cb[i].sel_id == id) {
printf("menu layout id = %x \n", id);
if (usr_menu_cb[i].window) {
if (usr_menu_cb[i].enter_check && (!usr_menu_cb[i].enter_check(usr_menu_cb[i].window))) {
return false;
}
UI_HIDE_CURR_WINDOW();
UI_SHOW_WINDOW(usr_menu_cb[i].window);
}
return true;
break;
}
}
return false;
}
//列表控件滑动功能底层以及做好,所以只需要做点击选择切页面功能
static int usr_test_grid_ontouch(void *ctr, struct element_touch_event *e)
{
struct ui_grid *grid = (struct ui_grid *)ctr;
int sel_item;
static u8 move_flag = 0;
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE:
break;
case ELM_EVENT_TOUCH_L_MOVE:
break;
case ELM_EVENT_TOUCH_MOVE:
move_flag = 1;
return false;//不接管消息
break;
case ELM_EVENT_TOUCH_DOWN:
move_flag = 0;
return false;//不接管消息
break;
case ELM_EVENT_TOUCH_UP:
if (move_flag) {
move_flag = 0;
return false;//不接管消息
}
return usr_menu_in_sel(grid);
break;
default:
return false;
break;
}
return true;//接管消息
}
//通过这种方式注册布局或者控件,实现软件控制UI各种动作
REGISTER_UI_EVENT_HANDLER(USR_TE_VERTLIST)
.onchange = usr_test_grid_onchane,
.onkey = NULL,
.ontouch = usr_test_grid_ontouch,
};
2.新建图片控件
同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后在需要的位置添加上图片控件,本示例将通过图片列表的方式添加多张图片,然后通过定时器不断的刷新切换不同的图片。
#define MAX_PIC_NUMBS 7
static void usr_pic_reflash(void)
{
static u8 CurrPicIndex = 0;
if(++CurrPicIndex >= MAX_PIC_NUMBS){
CurrPicIndex = 0;
}
ui_pic_show_image_by_id(USR_TE_PIC ,CurrPicIndex);
}
static int usr_test_pic_onchange(void *ctr, enum element_change_event e, void *arg)
{
struct ui_pic *pic = (struct ui_pic *)ctr;
static u16 pic_timer_id = 0;
switch (e) {
case ON_CHANGE_INIT:
if(!pic_timer_id){
pic_timer_id = sys_timer_add(NULL , usr_pic_reflash , 1000);
}
//默认进入显示第几张图片
ui_pic_set_image_index(pic ,2);
break;
case ON_CHANGE_FIRST_SHOW:
break;
case ON_CHANGE_RELEASE:
if(pic_timer_id){
sys_timer_del(pic_timer_id);
pic_timer_id = 0;
}
break;
default:
return false;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_PIC)
.onchange = usr_test_pic_onchange,
.onkey = NULL,
.ontouch = NULL,
};
static int usr_change_page_ontouch(void *ctr, struct element_touch_event *e)
{
struct window *window = (struct window *)ctr;
printf("%s:e->event=%d" , __func__ , e->event);
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE:
ui_send_event(KEY_CHANGE_PAGE , USR_TE_GRID_PAGE|BIT(31));
return false;
case ELM_EVENT_TOUCH_L_MOVE:
ui_send_event(KEY_CHANGE_PAGE , USR_TE_GRID_PAGE|BIT(31));
return false;
default:
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_PIC_PAGE)
.onchange = NULL,
.onkey = NULL,
.ontouch = usr_change_page_ontouch,
};
3.新建文本控件
文本控件格式分为ascii、strpic、text三种格式,strpic格式使用的是多国语言表中设置好的文本,一般会使用在菜单、标题等固定显示的页面;text格式是使用字库进行显示,一般使用在消息通知、歌词显示等不固定内容显示。同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后添加对应的文字控件。本示例将使用三个text格式的文本控件,通过数据源作为区分,分别显示蓝牙名、mac地址和版本号内容。
//text 格式显示
extern const u8 *bt_get_mac_addr();
extern const char *bt_get_local_name();
static u8 VStr[]="v1.0.0";
static int usr_test_text_onchange(void *ctr, enum element_change_event e, void *arg)
{
struct ui_text *text = (struct ui_text *)ctr;
u8 * buf;
u8 * addr_buf = NULL ;
u8 mac_addr[6] = {0};
switch (e) {
case ON_CHANGE_INIT:
if (!strcmp(text->source, "name")) {
buf = bt_get_local_name();
ui_text_set_text_attrs(text , buf , strlen(buf) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT);
}
else if (!strcmp(text->source, "addr")){
//log_info("set bt addr");
memcpy(mac_addr, bt_get_mac_addr(), 6);
put_buf(mac_addr ,6);
addr_buf = malloc (20);
if(addr_buf == NULL){
ASSERT(0, "addr_buf malloc fail");
}
for(u8 i = 0 ; i < 6 ; i++){
if(i == 0 ){
sprintf(addr_buf , "%X%c",mac_addr[i],':');
}
else if (i < 5){
sprintf(addr_buf , "%s%X%c",addr_buf ,mac_addr[i] ,':');
}
else{
sprintf(addr_buf , "%s%X",addr_buf ,mac_addr[i]);
}
}
ui_text_set_text_attrs(text , addr_buf , strlen(addr_buf) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT);
}
else if (!strcmp(text->source, "ver")){
ui_text_set_text_attrs(text , VStr , strlen(VStr) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT);
}
break;
case ON_CHANGE_RELEASE:
if(addr_buf){
free(addr_buf);
addr_buf = NULL;
}
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_NAME)
.onchange = usr_test_text_onchange,
.onkey = NULL,
.ontouch = NULL,
};
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_ADDR)
.onchange = usr_test_text_onchange,
.onkey = NULL,
.ontouch = NULL,
};
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_VER)
.onchange = usr_test_text_onchange,
.onkey = NULL,
.ontouch = NULL,
};
static int usr_change_page_ontouch(void *ctr, struct element_touch_event *e)
{
struct window *window = (struct window *)ctr;
printf("%s:e->event=%d" , __func__ , e->event);
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE:
UI_HIDE_CURR_WINDOW();
UI_SHOW_WINDOW(-1);
return false;
case ELM_EVENT_TOUCH_L_MOVE:
UI_HIDE_CURR_WINDOW();
UI_SHOW_WINDOW(-1);
return false;
default:
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_PAGE)
.onchange = NULL,
.onkey = NULL,
.ontouch = usr_change_page_ontouch,
};
4.新建数字控件
数字控件有TYPE_STRING和TYPE_NUM两种方式进行显示,两者区别在于TYPE_NUM格式最多只能显示xx/xx这样格式长度的内容,对于xx/xx/xx...这种多位数字格式的内容无法显示,这时候就可以TYPE_STRING进行显示。 同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后添加对应的数字控件。本示例将使用两个数字控件,通过获取RTC的时间,然后使用TYPE_STRING和TYPE_NUM两种方式显示不同长度内容信息。同样使用数据源的格式区分同一个页面中两个数字控件
static u8 stopwatch_num_buf[10] = {0};
static void get_sys_time(struct sys_time *time)
{
void *fd = dev_open("rtc", NULL);
if (!fd) {
memset(time, 0, sizeof(*time));
return;
}
dev_ioctl(fd, IOCTL_GET_SYS_TIME, (u32)time);
/* printf("get_sys_time : %d-%d-%d,%d:%d:%d\n", time->year, time->month, time->day, time->hour, time->min, time->sec); */
dev_close(fd);
}
void usr_string_reflash(void)
{
struct sys_time time;
struct unumber str_unumb;
struct unumber num_unumb;
get_sys_time(&time);
sprintf(stopwatch_num_buf, "%2d%c%2d%c%2d", time.hour, ':', time.min, '.', time.sec);
str_unumb.type = TYPE_STRING;
str_unumb.num_str = &stopwatch_num_buf;
num_unumb.type = TYPE_NUM;
num_unumb.numbs = 2;
num_unumb.number[0] = time.min;
num_unumb.number[1] = time.sec;
ui_number_update_by_id(USR_TE_NUMB_STR, &str_unumb);
ui_number_update_by_id(USR_TE_NUMB_NUM, &num_unumb);
}
static int usr_test_num_onchange(void *ctrl, enum element_change_event e, void *arge)
{
struct ui_number *num = (struct ui_number *) ctrl;
static u16 numb_timer_id = 0;
struct unumber unumb;
struct sys_time time;
switch (e) {
case ON_CHANGE_INIT:
if (!strcmp(num->source, "string")) {
get_sys_time(&time);
sprintf(stopwatch_num_buf, "%2d%c%2d%c%2d", time.hour, ':', time.min, '.', time.sec);
unumb.type = TYPE_STRING;
unumb.num_str = &stopwatch_num_buf;
ui_number_update(num, &unumb);
}
else if (!strcmp(num->source, "munb")) {
get_sys_time(&time);
unumb.type = TYPE_NUM;
unumb.numbs = 2;
unumb.number[0] = time.min;
unumb.number[1] = time.sec;
ui_number_update(num, &unumb);
}
if (!numb_timer_id) {
numb_timer_id = sys_timer_add(NULL, usr_string_reflash, 500);
}
break;
case ON_CHANGE_RELEASE:
if(numb_timer_id){
sys_timer_del(numb_timer_id);
numb_timer_id = 0;
}
break;
default:
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_NUM)
.onchange = usr_test_num_onchange,
.onkey = NULL,
.ontouch = NULL,
};
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_STR)
.onchange = usr_test_num_onchange,
.onkey = NULL,
.ontouch = NULL,
};
static int usr_change_page_ontouch(void *ctr, struct element_touch_event *e)
{
struct window *window = (struct window *)ctr;
printf("%s:e->event=%d" , __func__ , e->event);
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE:
ui_send_event(KEY_CHANGE_PAGE, 1);
return false;
case ELM_EVENT_TOUCH_L_MOVE:
ui_send_event(KEY_CHANGE_PAGE, 1);
return false;
default:
break;
}
return false;
}
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_PAGE)
.onchange = NULL,
.onkey = NULL,
.ontouch = usr_change_page_ontouch,
};