




已阅读5页,还剩20页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
声明:前阶段进行了近两个月的MTK平台上层开发,由于缺乏技术支持,对于整个平台的认识都是通过简略的文档和浅薄的经验摸索出来的。其间整理了一些文档。由于联发科提供的PDF全部是英文,有些名词难以翻译准确,只能凭单方理解和嵌入式开发的词汇习惯进行意译,还请谅解。系列文章均出自原创,肤浅可笑之处,望海涵。 (一)窗体的重画通过观察可以发现,每个窗体模板都调用这样一个函数:dm_redraw_category_screen()。这个函数便是显示窗体的函数。它内部的实现是这样的:获得该窗体所包含的组件及它们的属性,再根据组件的类型和属性,调用不同的接口,逐一绘制各组件。详细流程如下图所示:由上面的流程可见,无论是窗体所包含的组件,还是组件的属性,都是根据模板ID获取的。那么现在摆在面前的有两个问题:一、模板ID是如何传递到这个函数中的;二、模板ID和窗体组件、组件的属性,是如何关联到一起的。 我们逐一解决这两个问题。一、模板ID是如何传递到这个函数中的模板ID,是ShowCategory.Screen()过程中,所显示的界面的编号,千万不要与EntryNewScreen(scrID,) 函数中传入的窗口ID相混淆。它们以“MMI_”为前缀,被定义在枚举型结构MMI_CATEGORY_ID_LIST中,又通过结构体dm_data_struct和它的全局结构体变量g_dm_data,在应用程序中被广泛使用。先看看结构体dm_data_struct的定义:typedef structS32 s32ScrId;S32 s32CatId;S32 s32flags; 其中,s32ScrId是当前窗口ID,也就是我们使用EntryNewScreen()时传入的那个参数;而s32CatId才是模板ID;最后的flag,是模板需要显示软键盘、清屏等动作时,所置的标志变量,它在上面提到的那个dm_redraw_category_screen()函数中被判断。还是来重点看一下第2个结构体成员s32CateId的使用。 以 ShowCategory6Screen() 为例。这个显示模板的函数中,经常可以看到这样的语句:dm_data.s32ScrId = (S32)GetActiveScreenId();dm_data.s32CatId = MMI_CATEGORY6_ID;dm_data.s32flags = 0;dm_setup_data(&dm_data); 再看dm_setup_data()干了什么:void dm_setup_data(dm_data_struct *dm_data)g_dm_data.s32CatId = dm_data-s32CatId;g_dm_data.s32ScrId = dm_data-s32ScrId;g_dm_data.s32flags = dm_data-s32flags; 这样,MMI_CATEGORY6_ID就被很自然的赋到g_dm_data.s32ScrId中了,然后随着这个全局变量,顺利的被带到了dm_redraw_category_screen()中。 二、模板ID和窗体组件、组件的属性,是如何关联到一起的通过分析dm_search_control_set函数,发现窗体模板的组件和属性相关信息都隐藏在一个宏伟的结构体数组中:g_categories_controls_map。 这个结构体定义dm_category_id_control_set_map_struct定义如下:typedef structU16 category_id;U8 *control_set_p;S16 *default_coordinate_set_p;S16 *rotated_coordinate_set_p;dm_category_id_control_set_map_struct;第一个结构体成员,是窗体模板的ID;第二个结构体成员,是组件数组的首地址;第三个结构体成员,是默认的组件属性数组的首地址;第四个结构体成员,是特殊的组件属性数组的首地址。 dm_search_control_set()函数dm_search_coordinate_set()函数就是通过匹配模板ID在结构体数组中分别获取的组件集合和组件属性集合的。好,到这里,我们刚才提出的两个问题就明确了。(二)构成窗体的组件的定义 根据上文可以知道,窗体组件的定义与窗体模板ID是通过结构体dm_category_id_control_set_map_struct关联的。现在来看一看组件数组的结构。下面将以5号模板为例。const U8 category5 = 5, DM_BASE_LAYER_START, DM_SCR_BG, DM_BASE_CONTROL_SET1, DM_MULTILINE_INPUTBOX1, DM_CATEGORY_CONTROLLED_AREA在这个组件数组中,第一个字节“5”代表组件的数量;第二个字节开始就是组件的类别的ID了。比如说,DM_BASE_LAYER_START,代表开始使用Layer;DM_SCR_BG表示背景图;DM_BASE_CONTROL_SET1表示窗体的基本组成状态栏、标题和软按键;DM_MULTILINE_INPUTBOX1是多行输入框;DM_CATEGORY_CONTROLLED_AREA则是输入法的显示部分。它们被定义在枚举结构mmi_dm_control_ids_enum中。在MTK环境的原始版本中,一共有99种组件。其后的都是用户自己扩展的。各组件的外观和功用是什么,基本是可以见词知意的,可以在具体使用过程中了解。(三)各组件的属性定义仍以5号模板为例,观察组件属性数组。cosnt S16 coordinate_set = DM_FULL_SCREEN_COORDINATE_FLAG, DM_CONTENT_COORDINATE_FLAG, DM_FULL_SCREEN_COORDINATE_FLAG这两个常量都是代表组件属性的标志。定义在Wgui_draw_manager.h中大家可以观察到,它们都是负数:#define DM_FULL_SCREEN_COORDINATE_FLAG -10002#define DM_CONTENT_COORDINATE_FLAG -10004现在就让我们一起分析一下它们在控件绘制中起到的作用,以及定义成负数的原因。请回到(图1-1)。通过比对我们可以发现,在dm_redraw_category_screen()函数流程的第6步的循环中,就是通过判断组件的ID来实现逐步绘制窗体的,如: case DM_MULTILINE_INPUTBOX1: dm_setup_and_draw_multiline_inputbox(&UICtrlAccessPtr_p,&dm_cat_scr_info); 先来分析两个参数。联系上面的程序,我们可以知道,UICtrlAccessPtr_p是作为参数传入dm_get_cat_scr_coordinates()函数中的,又作为返回值,完成进入循环前的最后一次更改的,而此前,它指向的是组件属性数组。由于模板需要在其他组件被绘制前绘制窗体本身,因此它使用dm_get_cat_scr_coordinates()提前了解窗体的规格,而组件属性数组的第一个U16,就正是窗体的规格的标志ID:DM_FULL_SCREEN_COORDINATE_FLAG。根据该ID,dm_get_cat_scr_coordinates()做了分类判断,得到了指向结构体dm_cat_scr_info_struct的变量指针,其结构定义如下: typedef struct S16 x1; S16 y1; S16 x2; S16 y2; S16 flags;dm_cat_scr_info_struct;很明显,结构体成员分别是:起始、结束坐标,附加的标志。在dm_get_cat_scr_coordinates()过后,指针UICtrlAccessPtr_p向后偏移了两个字节,指向了DM_CONTENT_COORDINATE_FLAG。在绘制DM_BASE_LAYER_START、DM_SCR_BG、DM_BASE_CONTROL_SET1时,都不需要类似于起始位置、大小这样的属性来支持,因此它们的绘制函数dm_setup_base_layer()、dm_setup_and_draw_scr_bg()、dm_setup_and_draw_base_control_set()没有访问组件属性数组。而多行输入控件的绘制函数dm_setup_and_draw_multiline_inputbox()、和输入法显示区域的绘制函数dm_setup_and_draw_category_controlled_area() 却不约而同的调用了*UICtrlAccessPtr_p = dm_get_coordinates(*UICtrlAccessPtr_p, &dm_category_controlled_area_info)这一语句来获取组件属性。因此,DM_CONTENT_COORDINATE_FLAG是多行输入控件的属性标志,DM_FULL_SCREEN_COORDINATE_FLAG 是输入法显示区域的属性标志。最后观察一下dm_get_coordinates()函数(参见下页的图)。在这个函数里,对属性标志进行了判断,并最终为dm_cat_scr_info_struct结构体变量进行了赋值:可以看到,在dm_get_coordinates()函数中,对UICtrlAccessPtr_p指向的内容,也就是某一个组件属性标志常量,进行了判断。而后,根据不同情况对dm_coordinate_info的各成员赋了值。 因此,可以说,一个组件属性标志,就代表了一组包括组件坐标和标志在内的一组属性值。但是,要引起注意的是,这些值都是固定的。当组件的位置需要调整的时候,又该怎么办。继续观察dm_get_coordinates()的代码段。在判断了所有组件属性标志后,出现了一个else判断:else dm_coordinate_info-s16X = *UICtrlAccessPtr_p; UICtrlAccessPtr_p +; dm_coordinate_info-s16Y = * UICtrlAccessPtr_p; UICtrlAccessPtr_p +; dm_coordinate_info-s16Width = *UICtrlAccessPtr_p; UICtrlAccessPtr_p +; dm_coordinate_info-s16Height = *UICtrlAccessPtr_p; UICtrlAccessPtr_p +; dm_coordinate_info-Flags = *UICtrlAccessPtr_p; UICtrlAccessPtr_p +; 也就是说,UICtrlAccessPtr_p指向的值,如果不是任何一个属性标志常量的话,它就被认为是起始X的值,然后指针将被后移2字节,下一个值被认为是起始Y的值,以此类推,直到这个dm_coordinate_info赋值完毕。这就是自定义控件位置的方法。如果,要定义一组默认值,就可以先在Wgui_draw_manager.h中定义常量,然后在dm_get_coordinates()加入分支,当组件属性中包含这个常量时,就将dm_coordinate_info的各结构体成员赋值。 这就是构造一个窗体模板的原理和方法。利用这些,我们就可以利用GDI接口和GUI组件,随意的画我们自己的界面了,而不用仅仅去调用单调的ShowCategoryXXScreen()了。本文详细说明了如何建设一个自定义列表窗体模板。原理部分请参见MTK平台(1)如何添加一个窗体模板。最终实现的是一个字典输入界面。布局为:该模板不包含业务逻辑,仅提供页面显示和InputBox框输入事件后的ListBox的Redraw事件的注册,以及基本的输入法设置、清空后的返回函数。一、添加用户自定义列表模板的过程(一)在g_categories_controls_map中加入:,MMI_CATEGORY_CUSTOM_LIST,(U8*)custom_define_list,(s16*)coordinate_custom_list,NULLconst U8 custom_define_list= 5, DM_BASE_LAYER_START, DM_SCR_BG, DM_BASE_CONTROL_SET1, DM_SINGLELINE_INPUTBOX1, DM_LIST1;const S16 coordinate_custom_list= DM_FULL_SCREEN_COORDINATE_FLAG, DM_CUSTOM_DEFINE_INPUTBOX,/需要定义 DM_CUSTOM_DEFINE_LIST/需要定义;(二)在dm_get_coordinates()函数中加入:/设定列表位置和大小(不要忘记全局变量 MMI_custom_Listbox_x 等的定义)else if( *UICtrlAccessPtr_p = DM_CUSTOM_DEFINE_LIST )dm_coordinate_info-s16X = MMI_custom_Listbox_x;dm_coordinate_info-s16Y = MMI_custom_Listbox_y;dm_coordinate_info-s16Width = MMI_custom_Listbox_width;dm_coordinate_info-s16Height = MMI_custom_Listbox_height;dm_coordinate_info-Flags = DM_NO_FLAGS;UICtrlAccessPtr_p + ;/设定输入框位置和大小else if( *UICtrlAccessPtr_p = DM_CUSTOM_DEFINE_INPUTBOX )dm_coordinate_info-s16X = MMI_custom_inputbox_x ;dm_coordinate_info-s16Y = MMI_custom_inputbox_y;dm_coordinate_info-s16Width = MMI_custom_inputbox_width ;dm_coordinate_info-s16Height = MMI_custom_inputbox_height;dm_coordinate_info-Flags = DM_SINGLE_LINE_INPUTBOX_SPECIFIC_HEIGHT;UICtrlAccessPtr_p + ;(三)在Wgui_category.c中定义模板显示函数void ShowCategoryCustomListScreen( 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, 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(); SetupCategoryKeyHandlers(); MMI_title_string = (UI_string_type) title; MMI_title_icon = (PU8) get_image(title_icon); change_left_softkey(left_softkey, left_softkey_icon); change_right_softkey(right_softkey, right_softkey_icon);/Create List create_fixed_icontext_menuitems(); associate_fixed_icontext_list(); 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); for (i = 0; i = (MMI_singleline_inputbox.available_length - ENCODING_LENGTH) return; gui_single_line_input_box_insert_multitap_character(&MMI_singleline_inputbox, c); redraw_singleline_inputbox(); singleline_inputbox_input_callback(); singleline_inputbox_custom_input_callback();(五)Wgui_Category.c中添加用户事件定义接口/右键事件注册void SetCategoryCustomListRightSoftkeyFunction(void (*f) (void) wgui_singleline_inputbox_RSK_function = f;/key_0到key_9按下时的事件注册extern void (*singleline_inputbox_custom_input_callback) (void);void SetCategoryCustomListNumKeyFunction(void (*f) (void)singleline_inputbox_custom_input_callback = f ;/设置InputBox大小void SetCustomList_Inputbox_Size(S32 p_x , S32 p_y , S32 p_width , S32 p_height )MMI_custom_inputbox_x = p_x ;MMI_custom_inputbox_y = p_y ;MMI_custom_inputbox_width = p_width ;MMI_custom_inputbox_height = p_height ;/设置ListBox大小void SetCustomList_Listbox_Size(S32 p_x , S32 p_y , S32 p_width , S32 p_height )MMI_custom_Listbox_x = p_x ;MMI_custom_Listbox_y = p_y ;MMI_custom_Listbox_width = p_width ;MMI_custom_Listbox_height = p_height ;二、自定义列表模板的使用方法1、 调用SetCustomList_Inputbox_Size 和 SetCustomList_Listbox_Size 设置列表框和输入框的大小。2、 调用显示窗体的接口 ShowCategoryCustomListScreen。3、 调用右键事件注册函数,注册文本框被清空后的事件(如返回等)SetCategoryCustomListRightSoftkeyFunction。4、 调用key_0至key_9的事件注册函数,SetCategoryCustomListNumKeyFunction()。三、参数详细说明 void SetCustomList_Inputbox_Size(S32 p_x , S32 p_y , S32 p_width , S32 p_height ) 与 void SetCustomList_Listbox_Size(S32 p_x , S32 p_y , S32 p_width , S32 p_height )p_x , p_y :起始位置p_width , p_height : 大小。 void SetCategoryCustomListRightSoftkeyFunction(void (*f) (void) void SetCategoryCustomListNumKeyFunction(void (*f) (void) f(void) :函数地址。 void ShowCategoryCustomListScreen( U8 *title,/ 标题文本指针 U16 title_icon,/ 标题图标ID U16 left_softkey,/ 左键文本ID U16 left_softkey_icon,/ 左键图标ID U16 right_softkey,/ 右键文本ID U16 right_softkey_icon,/ 右键图标ID U8* custom_single_input_buffer,/ Input输入Buffer S32 number_of_items,/ 列表条目数 U8 *list_of_items,/ 列表项文本指针数组 U16 *list_of_icons,/ 列表项Icon S32 highlighted_item,/ 当前高亮显示的列表条目 U8 *history_buffer)/ 历史记录Buffer附:所需更改的文件wgui.cwgui_categories.cwgui_draw_manager.cwgui_inputs.cwgui.hwgui_categories_defs.hwgui_draw_manager.hCustCoordinate.c一、什么是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 (窗口号) FuncPtr entryFuncPtr;/(2)EntryNewScreen时要进入的 Entry_Func U8 inputBufferMAX_INPUT_BUFFER;/(3)没遇到过其使用,都是NULL。 U8 guiBufferMAX_GUI_BUFFER;/(4)窗体中控件的一些需保存的信息的Buffer,通常/在使用时被转化成各控件自定义的结构体如: list_menu_category_history。 history; 而存放data_A的类似于堆栈的数据区则以全局变量的形式定义在系统中:historyNode historyDataMAX_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) / (*currExitFuncPtr) ();/执行Exit_Func 第三段(记录Screen_ID,Exit_Func和EntryFunc):currExitScrnID = scrnID; currExitFuncPtr = exitFuncPtr; currEntryFuncPtr = entryFuncPtr;这样,我们就可以看出,EntryNewScreen函数先将上次执行EntryNewScreen时所记录的currExitScrnID, currEntryFuncPtr以history结构为载体记录入栈;然后执行了记录中的currExitFuncPtr;最后将本窗口的scrnID、exitFuncPtr、entryFuncPtr分别记录入全局变量currExitScrnID、currExitFuncPtr和currEntryFuncPtr,留待下次调用EntryNewScreen时使用。下面有数据出入栈流程,有兴趣的话可以跟踪一下。以先后顺序代表包含关系,如下: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(historyDatacurrHistoryIndex.guiBuffer);。参见出入栈流程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 =
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 呼吸机患者安全监护措施
- 妊娠合并症的护理管理查房
- 运动康复在糖尿病护理中的查房
- 静脉采血流程中的风险防控
- 工装瓷砖知识培训课件
- 工程采购课件
- 工程资料与分析课件
- 疫情线上班会课件
- 疫情期间激励班会课件
- 二零二五年度担保公司股权质押担保服务合同
- 2025中国成人ICU镇痛和镇静治疗指南解读
- 大学生心理健康十六讲(第3版) 课件 樊富珉 第1讲 心理健康知多少-大学生心理健康导论-第7讲 人际关系你我他-人际交往
- 曲靖市罗平县人民医院招聘考试真题2024
- 肾结石患者的护理
- 战术搜索教学课件
- 公园水面安全管理办法
- 2025年福建厦门港务控股集团有限公司招聘考试笔试试题(含答案)
- 2025年陕西省行政执法资格考试模拟卷及答案(题型)
- 2025年长三角湖州产业招聘笔试备考题库(带答案详解)
- 2025包头辅警考试真题
- SH∕T 3097-2017 石油化工静电接地设计规范
评论
0/150
提交评论