有些朋友可能已经注意到了,前面的代码中所用到的“show_status_info”方法找不到。在这一章中,我们对此进行补充说明。

在用户界面(UI)中,有一些元素是悬浮在页面场景(SCEAN)之上的,比如说状态栏,还有通知栏等。

在这里,我们编写一个主屏幕(MAINSCREEN),它主要用于显示操作提示、时间、网络连接状态等信息。

MainScreen.h

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、添加一个定时器,监控网络状态并更新时间

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、显示操作提示

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

#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、在主循环中渲染状态栏

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 的绘制方法中添加判断,如果超出了应用的显示范围,则修改相关标志位,以便后续刷新状态栏。

其三,不要使用定时器或中断来刷新状态栏,因为单片机本质上是单线程的,定时器和中断等都属于伪多线程,当它们同时进行渲染时,很容易出现花屏现象。

 

04-26 14:27