您好,欢迎来到保捱科技网。
搜索
您的当前位置:首页History管理机制

History管理机制

来源:保捱科技网
一、什么是History管理 对于我们上层用户而言,经常接触到的History管理是这样的: void EntryFunc() { U8 *guiBuffer; EntryNewScreen( Screen_ID , Exit_Func , Entry_Func , NULL ); guiBuffer = GetCurrGuiBuffer( SCR_ID_WORDMAIN_LIST ); ShowCategroyXXScreen( Title_ID , ¡ , guiBuffer); } 但是,无论是EntryNewScreen的调用,还是guiBuffer的传入,我们都很少考虑过对这些指针和函数在GUI的管理起到了什么样的作用。下面我们就要了解,以上的代码与History管理之间存在的关系。 在MTK环境中,每当我们进入一个窗口,系统将先提取前一个窗口需保留的数据。这些数据包括: 1. 窗口ID ; 2. 进入窗口时调用的函数和退出调用的函数 -- Exit_Func 和 Entry_Func ; 3. 组成窗体的控件的属性(如,列表控件当前高亮显示的条目、当前屏的首末条目等)。 举例说明这些数据在实际中是如何被使用的。 假设存在AB两个窗口,A窗口需要保留的数据为data_A。我们先从A窗口进入到B窗口。data_A将在B窗口调用EntryNewScreen()的时候,被压入一个结构类似于栈的数据存储区域;当从B调用GoBackHistory()返回A时,data_A从栈顶被弹出,然后A利用data_A将自身还原到其进入B之前的状态。 这就是History管理的作用。简言之,就是要保持窗口的外观状态。 二、History管理的机制 现在,我们来了解一下前面所说的data_A的数据结构是什么样的。 typedef struct _history { U16 scrnID; //(1)Screen ID (窗口号) //(2)EntryNewScreen时要进入的 Entry_Func FuncPtr entryFuncPtr; U8 inputBuffer[MAX_INPUT_BUFFER]; //(3)没遇到过其使用,都是NULL。 U8 guiBuffer[MAX_GUI_BUFFER]; - 1 - //(4)窗体中控件的一些需保存的信息的Buffer,通常//在使用时被转化成各控件自定义的结构体如: list_menu_category_history。 } history; 而存放data_A的类似于堆栈的数据区则以全局变量的形式定义在系统中: historyNode historyData[MAX_HISTORY]; (MAX_HISTORY = 50): 设当前窗口A所对应的数据是historyData[ EntryScreenNum ¨ 1 ] ,那么它是何时、是如何被赋值的?又是何时、如何被使用的? 经过跟踪调试,我们已经知道,在由窗口A进入到窗口B(调用EntryNewScreen)的时候,我们将data_A记录到了historyNode 的结构体变量中。但是,在EntryNewScreen的时候传入的,却是data_B,data_A是如何被记录和使用的呢? 我们摘选EntryNewScreen的子函数中所包含的较核心的代码来说明这个问题。这三段代码是按照现在的排放顺序来执行的。 第一段(history h 可理解为data_A): h.scrnID = scrnID; // scrnID = currExitScrnID h.entryFuncPtr = entryFuncPtr; // entryFuncPtr = currEntryFuncPtr pfnUnicodeStrcpy((S8*) h.inputBuffer, (S8*) & nHistory); // nHistory = NULL ; GetCategoryHistory(h.guiBuffer); //GetCategoryHistory是指向获取//guiBuffer的函数的指针 AddHistory(h); 第二段: if(currExitFuncPtr) { //¡ //执行Exit_Func //数据入栈 (*currExitFuncPtr) (); } 第三段(记录Screen_ID,Exit_Func和EntryFunc): currExitScrnID = scrnID; currExitFuncPtr = exitFuncPtr; currEntryFuncPtr = entryFuncPtr; 这样,我们就可以看出,EntryNewScreen函数先将上次执行EntryNewScreen时所记录的currExitScrnID, currEntryFuncPtr以history结构为载体记录入栈;然后执行了记录中的currExitFuncPtr;最后将本窗口的scrnID、exitFuncPtr、entryFuncPtr分别记录入全局变量currExitScrnID、currExitFuncPtr和currEntryFuncPtr,留待下次调用EntryNewScreen时使用。 - 2 - 下面有数据出入栈流程,有兴趣的话可以跟踪一下。以先后顺序代表包含关系,如下: 1.入栈(EntryNewScreen): (1)U8 EntryNewScreen(U16 newscrnID, FuncPtr newExitHandler, FuncPtr newEntryHandler, void *peerBuf) (2)static void ExecuteCurrExitHandler(void); (3)void ExecuteCurrExitHandler_Ext(void); (4)void GenericExitScreen( U16 scrnID , FuncPtr entryFuncPtr ); (5)void AddHistoryReference(history *addHistory); //处理historyData (6)S16 increment(); //更改栈指针 2.出栈(GoBackHistory): (1)void GoBackHistory(void); (2)static void ExecutePopHistory(void); //处理historyData (3)static U8 decrement(void); //更改栈指针 现在我们已经知道了history 的三个结构体成员是如何记录的了,最后来重点看一下history.guiBuffer是如何被记录和使用的。 三、GUI Buffer对控件属性的记录 由上2节我们知道,guiBuffer是窗体中某些控件的需保存的属性的Buffer,通常在使用时被转化成各控件自定义的结构体。如: list_menu_category_history。 现在有几个问题需要我们解答: 1. guiBuffer 指向的Buffer是如何被分配的?该块数据是动态的还是静态的? 2. 这块 Buffer 是何时被写入数据的? 3. 如何释放(动态分配时)或清空(固定地址时)该块 Buffer ? 让我们逐一解答上面的三个问题,以清晰我们对guiBuffer的认识。 1. 答:在void AddHistoryReference(history *addHistory)中,调用OslMalloc(MAX_GUI_BUFFER)动态申请了一块内存,用来保存在 GenericExitScreen 中获取的history.guiBuffer。[参见出入栈流程] 2. 如何释放(动态分配时)或清空(固定地址时)该块 Buffer ? 答 :在static void decrement (void)函数中,该buffer被释放: OslMfree(historyData[currHistoryIndex].guiBuffer);。[参见出入栈流程] - 3 - 3. 答 : 只要一个窗体模板有需要保存状态的控件,它们都调用了这个函数¡¡dm_setup_category_functions()。函数定义如下: void dm_setup_category_functions( FuncPtr redraw_function, U8 *(*get_history_function) (U8 *buffer), S32(*get_history_size_function) (void) ) { //指向窗体重画函数的函数指针 RedrawCategoryFunction = redraw_function; //指向获取窗体guiBuffer的函数指针 GetCategoryHistory = get_history_function; //指向获取窗体guiBuffer大小的函数指针 GetCategoryHistorySize = get_history_size_function; } 在只有一个控件的状态需要保存的窗体中,会这样传参给这个函数: dm_setup_category_functions(dm_redraw_category_screen, dm_get_category_history, dm_get_category_history_size); GenericExitScreen()函数中,将使用 GetCategoryHistory()获取某个控件的GuiBuffer[参见出入栈流程]。如果按照上面的设置,GetCategoryHistory指向了 dm_get_category_history这个函数。看看这个函数做了什么: control_set_ptr = dm_search_control_set((U16) p_dm_data->s32CatId, &coordinate_set_p); //获取窗体模板内的控件类型数组control_set_ptr u8NoOfUICtrls = control_set_ptr[0]; //获取数组内变量个数,即控件的个数 /*根据控件类型,获取控件的guiBuffer.值得注意的是,1.这里的histroy_Buffer的名称起的不好,应该起名为guiBuffer,不应混淆视听;2.最终history_buffer 将指向模板中定义的最后一个控件的guiBuffer*/ for (u8CtrlCt = 1; u8CtrlCt <= u8NoOfUICtrls; u8CtrlCt++) { switch (control_set_ptr[u8CtrlCt]) { case DM_CIRCULAR_MENU1: { get_circular_menu_category_history((U16) p_dm_data->s32CatId, history_buffer); break; } - 4 - case DM_LIST1: { get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer); break; } case DM_DYNAMIC_LIST1: { get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer); break; } case DM_ASYNCDYNAMIC_LIST1: { get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer); break; } //... } //... } 而在模版显示函数(ShowCategroyXXScreen)中,则根据 guibuffer 的情况设置控件的属性。如果 guibuffer 不为空,则说明该模板的显示函数是在GoBackHistory()的时候被调用的,而不是进入新窗口时被调用的。那么控件必然有一些保留的属性需要被还原。以6号窗口的List为例。在ShowCategory6Screen()中,调用下面的函数来恢复List设置: h_flag = set_list_menu_category_history(MMI_CATEGORY6_ID, history_buffer); 这样guiBuffer的Get和Set就统一起来了。 现在,我们已经知道了guiBuffer 所起到的作用。但是,如果一个窗体模板内有两个或两个以上需要记录状态的控件,又该怎么办呢? 四、灵活使用guiBuffer 在我们自己设计窗体模板时,经常会出现一个窗体中有多个控件的情况。但是,如果一个窗体中有两个控件、却依然调用dm_get_category_history()获取控件的GuiBuffer的话,就会出现问题。比如,我们在制作CustomList窗体时,初期使用了这样的代码: (1)模板中的组件设置: - 5 - const U8 custom_define_list[]= { 5, DM_BASE_LAYER_START, DM_SCR_BG, DM_BASE_CONTROL_SET1, DM_SINGLELINE_INPUTBOX1, DM_LIST1 }; //单行输入控件 //列表控件 (2)窗体显示函数 ShowCategoryCustomListScreen 部分源码: void ShowCategoryCustomListScreen(...,U8 * guiBuffer) { //... //根据 MMI_CATEGORY_CUSTOM_LIST 的 guiBuffer,为全局结构体变量 //MMI_fixed_list_menu赋值. h_flag = set_list_menu_category_history(MMI_CATEGORY_CUSTOM_LIST, guiBuffer); //而后利用MMI_fixed_list_menu,设置list的属性 if (h_flag) { fixed_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item); } else { } fixed_list_goto_item_no_redraw(highlighted_item); //... //再设置单行输入框的属性 wgui_setup_singleline_inputbox( 0, 0, 240, 320, custom_single_input_buffer, 50, MMI_CATEGORY_CUSTOM_LIST, get_string(right_softkey), get_image(right_softkey_icon), - 6 - INPUT_TYPE_ALPHANUMERIC_LOWERCASE| INPUT_TYPE_USE_ONLY_ENGLISH_MODES, //其中 wgui_setup_singleline_inputbox 函数中调用了 //set_singleline_inputbox_category_history()来解析guiBuffer //... dm_setup_category_functions(dm_redraw_category_screen, guiBuffer, 0); dm_get_category_history, dm_get_category_history_size); //... } 继续使用前3节的假设。窗口A使用了 CustomList 窗体模板。 从A 进入到B 时,EntryNewScreen函数调用了我们设置的获取guiBuffer函数dm_get_category_history ,它先保存了A中InputBox的属性,再保存A中List的属性 ¡¡ 此时它将把输入框的属性覆盖掉。当从B窗口返回到A窗口时,ShowCategoryCustomListScreen()函数先把history_buffer传给了 set_list_menu_category_history , 由于guiBuffer中存储的是List的数据,因此在交付fixed_list_goto_item_no_redraw 进行设置属性的时候,不会出现问题。但 wgui_setup_singleline_inputbox()就会因为guiBuffer中存储的不是输入框存储的数据而出现错误。 因此,权宜之计是,将更改传给wgui_setup_singleline_inputbox的入参: wgui_setup_singleline_inputbox( 0, 0, 240, 320, custom_single_input_buffer, 50, MMI_CATEGORY_CUSTOM_LIST, get_string(right_softkey), get_image(right_softkey_icon), INPUT_TYPE_ALPHANUMERIC_LOWERCASE | INPUT_TYPE_USE_ONLY_ENGLISH_MODES, NULL, 0); 这样,虽然死机 bug 避免了,但是特定情况下 InputBox 需要保存的属性,将全部丢失掉.因此,更加合适的做法是,提取 dm_setup_category_functions()中使用的函数接口: get_singleline_inputbox_category_history - 7 - get_list_menu_category_history 这样可以分别获取 inputbox 和 list 的属性,然后将得到的两个属性的数据连续存放在一块动态分配的Buffer中.如200号窗口的GetCategroyHistory函数所示: U8 *GetCategory200History(U8 *history_buffer) { S32 s; get_list_menu_category_history(MMI_CATEGORY200_ID, history_buffer); s = sizeof(list_menu_category_history); s = (s + 3) / 4; s *= 4; get_singleline_inputbox_category_history(MMI_CATEGORY200_ID, (U8*) (history_buffer + s), MMI_current_input_type); } 要注意的问题是,系统为guiBuffer分配空间时,依据的是 MAX_GUI_BUFFER,而不是 dm_setup_category_functions()所指定的获取guiBuffer大小的函数GetCategoryHistorySize.而且系统中从未使用过该函数指针所指向的函数。奇怪的是¡¡ 所有窗体模板的制作者都兢兢业业地制作了这个获取guiBuffer大小的函数。在200号窗口里,获取guiBuffer大小的函数如下: S32 GetCategory200HistorySize(void) { return (((sizeof(list_menu_category_history) + 3) / 4) * 4 + return (history_buffer); sizeof(singleline_inputbox_category_history)); } 如果怕出错且不怕麻烦的话,也可以未雨绸缪的写一个这样的函数,但恐怕多半是用不上的。 最后的任务就是在显示窗体时分别获取各控件的guiBuffer,然后将这些GUI_Buffer分别传给各个控件的Set函数. 仍然参看200号窗体的代码实现: h_flag = set_list_menu_category_history(MMI_CATEGORY200_ID, history_buffer); //第一个控件的Gui_Buffer if (h_flag) { S32 s = sizeof(list_menu_category_history); s = (s + 3) / 4; s *= 4; - 8 - dynamic_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item); wgui_setup_singleline_inputbox( input_box_x, (input_box_y), input_box_width, search_box_height, buffer, buffer_max_length, MMI_CATEGORY200_ID, get_string(right_softkey), get_image(right_softkey_icon), INPUT_TYPE_MMI_MULTITAP_BPMF, (U8*) (history_buffer + s), 1); //第二个控件的GUI_Buffer起始 五、小结 经过这些研究和借鉴.我们在窗体开发工作中所需的技术点已经逐步趋于完善了。后期开发工作中,可能还有一些GUI相关的探索工作需要进行。据现在的情况来看,主要有两方面: 1.inline控件及57号窗体的进一步研究。 必要性很明显。现在使用中的NumberTune就很有必要嵌入到这个57号模板中。对于Inline控件的管理方式的研究是这个工作的前提。 2.touch panel的相关研究。 主要看控件的touch panel编译开关内的代码实现。将有助于平台移植。 3.现有控件的进一步熟悉。 如果有可利用的现有控件的话,尽量避免移植自己的控件上来。这对于系统的稳定和GUI的移植都不利。 附1:其他 History Info 结构体 typedef struct _history { U16 scrnID; - 9 - FuncPtr entryFuncPtr; U8 inputBuffer[MAX_INPUT_BUFFER]; U8 guiBuffer[MAX_GUI_BUFFER]; } history; typedef struct _historyCallback { U16 scrnID; HistoryDelCBPtr historydelCBPtr; } historyCallback; 附2:函数接口 (1)void AddHistory(history addHistory); //添加历史记录节点 (2)void GoBackHistory(void); //删除历史记录堆栈顶端节点,并执行EntryFunction(EntryNewScreen指定的函数) (3)void DeleteNHistory(U16 DeleteCount); //删除N个节点 (4)U8 GetHistory(U16 ScreenID,history *ptrHistory); //通过窗口号获取历史记录数据 (5)U8 GoBackToHistory(U16 ScreenID); //返回到指定的窗口号 (6)void GoBacknHistory(U16 nCount); //删除nCount个历史记录节点,并执行第nCount+1个EntryFunction (7)U8* GetCurrGuiBuffer(U16 ScreenID); //按窗口号获取指定的Gui_Buffer (8)U8* GetCurrInputBuffer(U16 ScreenID); //按窗口号获取指定的Input_Buffer (9)void ExecutecurrHisIndEntryFunc(void); //获取当前在栈顶的历史记录节点的EntryFunction. (10)U8 GoBeyondMarkerScr(U16 ScreenID); //删除从当前窗口号到指定窗口号之间的一切历史记录节点,并执行最近的EntryFuntion (11)U8 DeleteUptoScrID(U16 ScreenID); //删除从当前窗口号到指定窗口 - 10 -

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- baoaiwan.cn 版权所有 赣ICP备2024042794号-3

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务