1、关于窗体(category screen)mtk 的控件和窗体绘制机制以及控件、窗体的事件响应机制一、 entryfunction在文档里面我们看到下面的条款:以下引自:write applications using pixtel MMI platform.pdfBefore display the new screen over previous screen the following must be executed:1. save the contents of previous screen.保存前面那个窗体的内容;2. get the buffer to store the co
2、ntents of screen to displayed.获得足够的缓存空间来保存当前要显示的窗体的内容。3. get display attribute for the following screen, i.e. item to display as lists, circular menu etc.获得下一个要显示的窗体的属性;4. retrieve number of submenu items to be displayed.获取要显示的子菜单条目的个数;5. set the parent of new screen to be displayed.设置要被显示的新窗体的父窗体;6
3、. set the submenu item to be displayed highlighted, on next screen.设置下一级子菜单要被高亮显示的条目;7. set the function to be executed on pressing right or left soft key.设置按下左右软件的执行函数;8. set the function to be called on exiting the next screen.设置退出下个窗体的的调用函数;这些工作一般都在窗体的入口函数里面实现的内容,因此我们有必要先从窗体的入口函数开始。以一段代码来说明(这里省略了
4、很多的东西):void EntryScrIncomingOptions(void)1、退出上一窗口,进入新的窗口;EntryNewScreen(ITEM_SCR_INCOMING_OPTIONS,NULL, EntryScrIncomingOptions, NULL);2、获取当前窗口的 GUI buffer guiBuffer = GetCurrGuiBuffer(ITEM_SCR_INCOMING_OPTIONS); 3、获取列表窗口的子菜单数目;number_of_items = GetNumOfChild_Ext(MITEM_OPT_PSEUDO);4、获取要显示的字符串序列;GetS
5、equenceStringIds_Ext(MITEM_OPT_PSEUDO, list_of_items); 5、设置当前窗口的父窗口的 ID;SetParentHandler(MITEM_OPT_PSEUDO);6、注册 highlight 函数RegisterHighlightHandler(ExecuteCurrHiliteHandler);7、在已经获取了以上信息后,绘制当前的窗口;ShowCategory1Screen(STR_SCR1002_CAPTION,0,STR_GLOBAL_OK,IMG_GLOBAL_OK,STR_GLOBAL_BACK,IMG_GLOBAL_BACK,n
6、umber_of_items,list_of_items,0,guiBuffer);8、后面是注册按键函数;SetRightSoftkeyFunction(GoBackHistory, KEY_EVENT_UP);SetKeyHandler(KbCBackCallIncomingRejected, KEY_END, KEY_EVENT_DOWN);SetKeyHandler(KbCBackCallIncomingAccepted, KEY_SEND, KEY_EVENT_DOWN);SetSideVolumeKeysForCM();SetKeyHandler(GoBackHistory, KE
7、Y_LEFT_ARROW, KEY_EVENT_DOWN);这是一个典型的窗口入口函数,窗口的进入都是通过类似的这种 entryfunction() 来实现。这时引来另外一个问题,就是关于窗口切换是通过什么来实现的,怎么才能保证这些窗口在切换的过程中保持当时的状态。这就涉及到 history 管理。History 的管理是通过栈来实现的。在文档中,我们读到这样的内容:History concept is implemented as a stack. Whenever a new history node is added to the list, it is added at the beg
8、inning and whenever a node is deleted it is deleted form the beginning.历史这个概念是用栈来实现的。当一个新的历史节点加入到这个列表时,它就被加到栈顶,但这个节点被删除时,它就被从栈顶删除。一般来说历史的应用步骤是这样的:1. saving screen contents to history.保存窗体的数据到历史中去。这个数据的内容将在下面提到。2. going back N levers in history and redrawing screen at that depth.返回到 N 级历史,并重画当时的窗体。3.
9、 deleting N nodes from top of stack from history.从历史的栈顶删除 N 个节点。还是这样一个函数:void EntryFunc()EntryNewScreen( Screen_ID , Exit_Func , Entry_Func , NULL );guiBuffer = GetCurrGuiBuffer( SCR_ID_WORDMAIN_LIST );ShowCategroyXXScreen( Title_ID , , guiBuffer);这里无论是 EntryNewScreen 的调用,还是 guiBuffer 的传入,我们都很少考虑过对这
10、些指针和函数在 GUI 的管理起到了什么样的作用。下面我们就要了解,以上的代码与 History 管理之间存在的关系。在 MTK 环境中,每当我们进入一个窗口,系统将先提取前一个窗口需保留的数据。这些数据包括:1. 窗口 ID ;2. 进入窗口时调用的函数和退出调用的函数 - Exit_Func 和 Entry_Func ;3. 组成窗体的控件的属性( 如,列表控件当前高亮显示的条目、当前屏的首末条目等)。通过跟踪调试程序可以看到 这些动作是在每次调用 EntryNewScreen 函数的时候实现的。具体的实现过程就是 :EntryNewScreen 函数先将上次执行 EntryNewScre
11、en 时所记录的 currExitScrnID, currEntryFuncPtr 以 history 结构为载体记录入栈;然后执行了记录中的 currExitFuncPtr; 最后将本窗口的 scrnID、 exitFuncPtr、 entryFuncPtr 分别记录入全局变量currExitScrnID、 currExitFuncPtr 和 currEntryFuncPtr,留待下次调用 EntryNewScreen 时使用。在退出本窗口的时候通过GoBackHistory 出栈,返回到本窗口的父窗口。举例说明这些数据在实际中是如何被使用的。假设存在 AB 两个窗口,A 窗口需要保留的数据
12、为 data_A。我们先从 A 窗口进入到 B 窗口。data_A 将在 B 窗口调用EntryNewScreen()的时候,被压入一个结构类似于栈的数据存储区域;当从 B 调用 GoBackHistory()返回 A 时,data_A 从栈顶被弹出,然后 A 利用 data_A 将自身还原到其进入 B 之前的状态。这就是 History 管理的作用。简言之,就是要保持窗口的外观状态。到这里,我们就回答了前面所提出的问题。二、 窗口的绘制绘制窗体实际上可以看作是绘制轮廓和绘制组件两个部分。我们先看这么一个文件 CustCoordinates.c 这个文件主要是定义了这么一个宏 g_catego
13、ries_controls_map:或者类似于这样的定义,这个数组就具体的将窗体的轮廓以及窗体的各个组件的位置作出了定义下面我们以一个具体的例子作为说明:const U8 category5 = 5, / 这个代表组件的个数;DM_BASE_LAYER_START, / 开始层;DM_SCR_BG, / 背景;DM_BASE_CONTROL_SET1, /表 示 窗 体 的 基 本 组 成 状 态 栏 、 标 题 和 软 按 键DM_MULTILINE_INPUTBOX1, /多 行 输 入 框DM_CATEGORY_CONTROLLED_AREA / 输 入 法 的 部 分 ;;这些组件被定
14、义在 枚 举 结 构 mmi_dm_control_ids_enum中 。const S16 coordinate_set5 = DM_FULL_SCREEN_COORDINATE_FLAG,DM_CONTENT_COORDINATE_FLAG,DM_FULL_SCREEN_COORDINATE_FLAG;这个数组是这些组件的属性;这个属性主要是指各个组件的坐标,高度,宽度;这些数组里面定义的内容通过 dm_get_coordinates() 这个函数映射到 具体的绘制过程中;在 dm_get_coordinates 这个函数中我们可以看到,这些 DM_FULL_SCREEN_COORDINA
15、TE_FLAG 常量实际上都被转化为坐标。现在我们回到刚才的那个函数 ShowCategoryScreen() 中来 。 下 图 是 个 典 型 的 窗 口 图 :以 下 为 例 子 :void ShowCategory353Screen(U8 *title,U16 title_icon,U16 left_softkey,U16 left_softkey_icon,U16 right_softkey,U16 right_softkey_icon,S32 number_of_items,U8 *list_of_items,U16 *list_of_icons,U8 *list_of_descri
16、ptions,S32 flags,S32 highlighted_item,U8 *history_buffer)/*-*/* Local Variables */*-*/dm_data_struct dm_data;S32 i;U8 h_flag;/*-*/* Code Body */*-*/gdi_layer_lock_frame_buffer(); / 锁定当前层的 buffer;/ 创建一个列表式的窗口create_fixed_icontext_menuitems();associate_fixed_icontext_list();/ 并将窗口下的内容,包括标题栏,左右软件,以及各个子
17、菜单的图标 显示出来; ShowListCategoryScreen(UI_string_type) title,get_image(title_icon),get_string(left_softkey),get_image(left_softkey_icon),get_string(right_softkey),get_image(right_softkey_icon),number_of_items);if (list_of_descriptions = NULL)for (i = 0; i number_of_items; i+)add_fixed_icontext_item(UI_s
18、tring_type) list_of_itemsi, wgui_get_list_menu_icon(i, list_of_iconsi);wgui_pop_up_description_stringsi.text_strings0 = NULL;elsefor (i = 0; i number_of_items; i+)add_fixed_icontext_item(UI_string_type) list_of_itemsi, wgui_get_list_menu_icon(i, list_of_iconsi);wgui_pop_up_description_stringsi.text_
19、strings0 = (UI_string_type) list_of_descriptionsi;h_flag = set_list_menu_category_history(MMI_CATEGORY52_ID, history_buffer);/ 高亮当前选中的 ITEMif (h_flag)fixed_list_goto_item_no_redraw(MMI_fixed_list_menu.highlighted_item);elsefixed_list_goto_item_no_redraw(highlighted_item);MMI_title_string = (UI_strin
20、g_type) title;MMI_title_icon = (PU8) get_image(title_icon);set_pop_up_descriptions(1, number_of_items, MMI_fixed_list_menu.highlighted_item);gdi_layer_unlock_frame_buffer();/ 前面这整个一段是用来绘制一个列表窗口,这部分的内容基本上根据自己的选择有什么画什么;/ 而下面的内容是每个窗体都共有的部分;ExitCategoryFunction = ExitListCategoryScreen;dm_setup_category
21、_functions(dm_redraw_category_screen,dm_get_category_history, dm_get_category_history_size);dm_data.s32ScrId = (S32) GetActiveScreenId();dm_data.s32CatId = MMI_CATEGORY52_ID;dm_data.s32flags = 0;dm_setup_data(dm_redraw_category_screen(); /* end of ShowCategory353Screen */因为 MTK 后面的代码的 gui 部分是用 draw_
22、manager 这个来控制的,因此所有的窗口的控件的实际绘制过程都是通过 dm_redraw_category_screen(); 这个函数来实现的;这个函数可以帮助我们绘制一些比较特殊的自己需要的组件,当然如果我们使用的组件已经包含在这个函数里面,那么直接使用。可以分析一下这个函数的流程:获取窗体模版的 ID; / 这个在 showcategory 里面实现,dm_data.s32CatId = MMI_CATEGORY52_ID;根据模版的 ID 得到组件的集合和个数;/ control_set_ptr = dm_search_control_set(U16) g_dm_data.s32C
23、atId, 根据模版 ID 得到组件属性标识的集合; / UICtrlAccessPtr_p = dm_search_coordinate_set(g_dm_data.s32ScrId);锁定当前的 frame,各个组件绘制前的任务准备就绪;/ gdi_layer_lock_frame_buffer();绘制窗体之前,还可以改变窗体的大小;/ UICtrlAccessPtr_p = dm_get_cat_scr_coordinates(UICtrlAccessPtr_p, 获取不同组件的不同属性,并根据不同的属性绘制出自己需要的窗口;/ for (u8CtrlCt = 1; u8CtrlCt = u8NoOfUICtrls; u8CtrlCt+) 这个语句包含的内容全部绘制完毕,整合 GDI layer,将当前 frame 解锁;上面已经说明了一个窗体的绘制过程。另外与窗体相关的函数还有:Redrawcategory*screen(); 这个是窗体的刷新函数;Exitcategory*screen() ; 这个是窗体的退出函数;