有些朋友可能已经注意到了,前面的代码中所用到的"show_status_info"方法找不到。在这一章中,我们对此进行补充说明。
在用户界面(UI)中,有一些元素是悬浮在页面场景(SCEAN)之上的,比如说状态栏,还有通知栏等。
在这里,我们编写一个主屏幕(MAINSCREEN),它主要用于显示操作提示、时间、网络连接状态等信息。
MainScreen.h
cpp
typedef struct StatusUpdateTask{
u8 type;
u8 data1;
u8 data2;
u8 data3;
} StatusUpdateTask;
void init_screen();
void show_status_info(const char *format, ...);
void clear_screen();
void handleMainScreanTask();
实现:
1、添加一个定时器,监控网络状态并更新时间
cpp
void timer_int_irq(u8 *arg)
{
int netS = isNetworkOk();
if(last_net_Status !=netS){
updateNetStatus.type = 1;
updateNetStatus.data1 = netS;
last_net_Status =netS;
}
if(isNtpOk()){
tls_get_rtc(tblock);
updateTitleTime.type = 2;
updateTitleTime.data1 = tblock->tm_hour;
updateTitleTime.data2 = tblock->tm_min;
updateTitleTime.data3 = tblock->tm_sec;
}
}
int sys_timer_init(void)
{
u8 timer_id;
struct tls_timer_cfg timer_cfg;
timer_cfg.unit = TLS_TIMER_UNIT_MS;
timer_cfg.timeout = 400;
timer_cfg.is_repeat = 1;
timer_cfg.callback = (tls_timer_irq_callback)timer_int_irq;
timer_cfg.arg = NULL;
timer_id = tls_timer_create(&timer_cfg);
tls_timer_start(timer_id);
printf("timer start\n");
return WM_SUCCESS;
}
2、显示操作提示
cpp
void show_status_info(const char *format, ...){
memset(str_buff, 0, 128);
va_list args;
va_start(args, format);
vsprintf(str_buff, format, args);
va_end(args);
Display_Fill_Rectangle(0, SCREEN_HEIGHT - _statusbar_height ,SCREEN_WIDTH - 91 ,SCREEN_HEIGHT -1,_statusbar_back_color);
Display_String(10, SCREEN_HEIGHT - _statusbar_height +1, &mainScreenOption1, str_buff);
}
完整代码
MainScreen.cpp
cpp
#include "string.h"
#include "MainScreen.h"
#include "MainScreenImages.h"
#include "stdarg.h"
#include "stdio.h"
#include "../src/driver/net.h"
#include "../lib/DList.h"
#include "wm_timer.h"
#define _statusbar_height 20
#define _statusbar_back_color BLUE
#define _statusbar_front_color WHITE
char str_buff[128];
struct StatusUpdateTask updateNetStatus;
struct StatusUpdateTask updateTitleTime;
int last_net_Status;
char tmBuff[9];
struct tm *tblock;
DisplayOption mainScreenOption1 = {FONT_SIZE_1516, _statusbar_front_color, _statusbar_back_color, 0, 0};
void timer_int_irq(u8 *arg)
{
int netS = isNetworkOk();
if(last_net_Status !=netS){
updateNetStatus.type = 1;
updateNetStatus.data1 = netS;
last_net_Status =netS;
}
if(isNtpOk()){
tls_get_rtc(tblock);
updateTitleTime.type = 2;
updateTitleTime.data1 = tblock->tm_hour;
updateTitleTime.data2 = tblock->tm_min;
updateTitleTime.data3 = tblock->tm_sec;
}
}
int sys_timer_init(void)
{
u8 timer_id;
struct tls_timer_cfg timer_cfg;
timer_cfg.unit = TLS_TIMER_UNIT_MS;
timer_cfg.timeout = 400;
timer_cfg.is_repeat = 1;
timer_cfg.callback = (tls_timer_irq_callback)timer_int_irq;
timer_cfg.arg = NULL;
timer_id = tls_timer_create(&timer_cfg);
tls_timer_start(timer_id);
printf("timer start\n");
return WM_SUCCESS;
}
void setNetStatus(u8 sta){
if(sta){
Display_Show_Picture(SCREEN_WIDTH - 22, SCREEN_HEIGHT - _statusbar_height +1, 16, 16, gImage_FullSig);
}
else{
Display_Show_Picture(SCREEN_WIDTH - 22, SCREEN_HEIGHT - _statusbar_height +1, 16, 16, gImage_NoSig);
}
}
void show_Title_Time(char* tm){
Display_String(SCREEN_WIDTH - 90, SCREEN_HEIGHT - _statusbar_height +1, &mainScreenOption1, tm);
}
void init_screen(){
Display_Fill_Rectangle(0, SCREEN_HEIGHT - _statusbar_height ,SCREEN_WIDTH ,SCREEN_HEIGHT ,_statusbar_back_color);
Display_Fill_Rectangle(0,0 ,SCREEN_WIDTH,SCREEN_HEIGHT -_statusbar_height ,BLACK);
setNetStatus(0);
tblock = new struct tm();
sys_timer_init();
}
void clear_screen(){
Display_Fill_Rectangle(0,0 ,SCREEN_WIDTH,SCREEN_HEIGHT -_statusbar_height ,BLACK);
}
void init_status_bar(){
}
void show_status_info(const char *format, ...){
memset(str_buff, 0, 128);
va_list args;
va_start(args, format);
vsprintf(str_buff, format, args);
va_end(args);
Display_Fill_Rectangle(0, SCREEN_HEIGHT - _statusbar_height ,SCREEN_WIDTH - 91 ,SCREEN_HEIGHT -1,_statusbar_back_color);
Display_String(10, SCREEN_HEIGHT - _statusbar_height +1, &mainScreenOption1, str_buff);
}
u8 _MainScreanTaskLock=0;
void handleMainScreanTask(){
if(updateNetStatus.type){
setNetStatus(updateNetStatus.data1);
updateNetStatus.type =0;
}
if(updateTitleTime.type){
sprintf(str_buff, "%02d:%02d:%02d",updateTitleTime.data1,updateTitleTime.data2,updateTitleTime.data3);
show_Title_Time(str_buff);
updateTitleTime.type =0;
}
}
3、在主循环中渲染状态栏
cpp
void UserMain(void)
{
。。。
while(1){
IScean* cur= ((IScean*)sceanList->prev->data);
_tmp_Run_Ticks = tls_os_get_time();
keyAdepterProc(_tmp_Run_Ticks - _last_Run_Ticks);
res = cur->tick(_tmp_Run_Ticks - _last_Run_Ticks);
_last_Run_Ticks = _tmp_Run_Ticks;
switch (res) {
case SceanResult_EXIT:
delete cur;
ListPopBack(sceanList);
((IScean*)sceanList->prev->data)->scean_resume();
break;
case SceanResult_Done:
break;
}
FrameCount++;
handleMainScreanTask();
}
}
在实际操作中,有一些需要特别注意的点,以避免陷入误区:
首先,要先渲染应用,然后再绘制状态栏,这样才能保证状态栏浮在应用之上。
其次,可以为应用设置一个配置项,来表明是否为全屏应用,如果是,则无需渲染状态栏。
再者,状态栏通常是根据实际需求进行绘制的,以便提高整体性能。
即便做到了以上这些,还是有可能出现状态栏被覆盖的情况。对此,我们可以考虑以下解决方法:
其一,采用图层缓存的方式,让状态栏的图层位于应用图层之上,但这可能会带来性能方面的问题。
其二,在 LCD 的绘制方法中添加判断,如果超出了应用的显示范围,则修改相关标志位,以便后续刷新状态栏。
其三,不要使用定时器或中断来刷新状态栏,因为单片机本质上是单线程的,定时器和中断等都属于伪多线程,当它们同时进行渲染时,很容易出现花屏现象。