一:MTK原列表菜单的实现方式
列表界面,有一个结构体变量记录列表菜单的各种信息MMI_fixed_list_menu,对其中几个成员说明
MMI_fixed_list_menu.x,MMI_fixed_list_menu.y为列表菜单显示区域的起始坐标,
MMI_fixed_list_menu.width,MMI_fixed_list_menu.height为列表菜单显示区域的宽和高,
MMI_fixed_list_menu.n_items为列表菜单的item个数,
MMI_fixed_list_menu.first_displayed_item为显示区域的第一个显示的item index值,
first_displayed_item的值为0到n_items-1,
MMI_fixed_list_menu.last_displayed_item为显示区域的最后一个显示的item index值,
MMI_fixed_list_menu.highlighted_item为当前高亮的itemindex值,
原来MTK列表界面绘制原理是这样的,从列表的MMI_fixed_list_menu. first_displayed_item开始,一条一条菜单绘制,用一个初始化为0的变量total_height记录绘制的菜单高度,每绘制一个item,total_height的值就增加一个item的高度,每次绘制item之前,会对total_height和MMI_fixed_list_menu.height进行比较,发现total_height>=MMI_fixed_list_menu.height就中断绘制,其流程如下:
fixed_list_menu *m= &MMI_fixed_list_menu;//用一个指针m指向MMI_fixed_list_menu
counter = 0//计数器
for (i =m->first_displayed_item; (i < m->n_items && !done); i++)
{
total_height += iheight;
if (total_height < list_height + 1)
{
item_display_function(i);//绘制第i个item
counter++;
}
}
m->last_displayed_item= m->first_displayed_item + counter – 1//计算最后一个菜单item值
这种显示方式,会带来一个问题,在滑动菜单上特别明显,严重影响了酷炫体验,一是滑动列表菜单时,不能将菜单停留在任意位置,first_displayed_item的起始位置,始终在标题栏的下方开始的位置,不能出现第一个item显示一部分,最后一个item显示一部分这种情况,当用手拖动列第一个item没有和标题栏下方对齐时,菜单就会强制对齐,这样列表界面就有明显的跳跃现象,滑动的坐标就不是连续的。
二:
1:为了解决上述问题,对MTK的列表菜单显示机制做了修改,在MMI_fixed_list_menu结构体里面,添加一个成员变量MMI_fixed_list_menu.menu_offset_y,用来记录第一个菜单相对列表界面起始位置MMI_fixed_list_menu.y的偏移量,我们知道,进入一个新菜单后,原来的菜单信息需要保存历史,这样回来的时候,保证显示的位置和原来的一直,所以MMI_fixed_list_menu.menu_offset_y需要像MMI_fixed_list_menu. first_displayed_item一样,保存到历史中去。
通过上图详细讲解新的列表显示机制。
假如进入一个列表界面,该列总item数为10,当初次进入该列表时,item从item1显示到item6,每个item的高度为item_height,当触摸笔从down_y滑动到up_y时
m->menu_offset_y= down_y - up_y;
根据menu_offset_y计算第first_displayed_item和last_displayed_item
m->first_displayed_item= m->menu_offset_y/item_height;
m->last_displayed_item = (m->menu_offset_y + m->height - 1) /item_height;
根据menu_offset_y计算first_displayed_item和m->y的偏移值item_offset_y;
item_offset_y =m->menu_offset_y%item_height;
这时候列表显示应该是从item1到item8,这样就有两个item需要特殊处理,item1和item8,因为列表和状态栏,标题栏,按键栏属于同一个layer,都是base layer,直接绘制item1和item8的绿色部分就会将标题栏和按键栏覆盖掉。当列表绘制,判断为fist item和last item时,不用原来item_display_function函数处理,通过自己添加的一个函数item_copy_handle处理,在item_copy_handle函数中,新建一个new layer,在new layer上绘制第一个item和最后一个item,然后把上图中红色部分flatten到base layer上。这样就显示出了item1到item8,并且实现了item的部分显示,这样就可以做到菜单连续坐标的平滑效果,红色flatten区域的计算
显示第一个item的flatten区域计算:
flatten_x1 =m->x,;
flatten_y1 =m->y;
flatten_x2 =m->x + m->width – 1;
flatten_y2 =m->y + (item_height - menu_offset_y);
显示最后一个item的flatten区域计算
flatten_x1 =m->x,;
flatten_y1 =(m->y +m->height-1) - menu_offset_y;
flatten_x2 =m->x + m->width – 1;
flatten_y2 = m->y+m->height-1;
新的列表菜单绘制流程伪代码如下:
for (i =m->first_displayed_item; i <=m->last_displayed_item; i++)
{
if(i==m->first_displayed_item || i==m->last_displayed_item)
{
item_copy_handle(i);//特殊处理first item和last item
}
else
{
item_display_function(i);//绘制第i个item
}
}
2:关于自由滑动的实现,自由滑动就是触摸笔在列表上快速滑动一段距离后,离开触摸屏时,菜单会继续朝触摸笔滑动的方向自由滑动一段距离。
实现方式如下:在滑动过程中,用一个数组不断记录最近50个move事件的时间和位置,当触摸笔离开触摸屏时,对这50个move事件信息进行处理,通过相邻两个move事件的时间delta_time和距离delta_y,计算出最快的move事件,即delta_y/delta_time的值最大,
将delta_y/delta_time再乘一个系数RUN_REFRESH_SCREEN_INTERVAL作为自由运动量free_run_momentum,滑动方向为从屏幕下方往上方滑动时,free_run_momentum为正,往屏幕下方滑动时,free_run_momentum为负。然后启动一个定时器不断刷新列表显示,通过不做设置m->menu_offset_y = m->menu_offset_y+free_run_momentum实现整个列表往前或往后自由滚动 ,每次刷新,将free_run_momentum乘一个衰减系数REDUCE_COEFFICIENT实现不断的衰减,实现列表界面的滑动由快变慢效果,一直衰减到free_run_momentum为0为止。通过调整系数RUN_REFRESH_SCREEN_INTERVAL和衰减系数就可以调节滑动的快慢效果。
3:关于过冲实现:最大过冲距离为m->height的1/3,这个值可以随意调整
过冲效果示意图如下:
过冲1,第一个item下拉到最大距离low_bound, low_bound = -(m->height/3);
过冲2,最后一个item上拉到最大距离hight_bound,
hight_bound =(m->n_items * item_height) - m->heigh + (m->height/3);
过冲1时菜单往回滑动过程:
往下拖动菜单到最大位置,这时候m->menu_offset_y值为low_bound,触摸笔离开触摸屏时,设置菜单最终应回的位置的g_gui_ssp_free_run_final_pos值和自由滚动量
free_run_momentum值,
free_run_momentum= low_bound;
g_gui_ssp_free_run_final_pos= 0;
然后启动定时器,通过不断计算m->menu_offset_y值和刷屏实现菜单往回滑动,
m->menu_offset_y的计算方法如下:
m->menu_offset_y=g_gui_ssp_free_run_final_pos+free_run_momentum*REDUCE_COEFFICIENT
最终菜单往回滑动到第一个item与标题栏对齐
过冲2时菜单往回滑动过程:
往上拖动菜单到最大位置,这是这时候m->menu_offset_y值为height_bound,触摸笔离开触摸屏时,设置菜单最终应回的位置的g_gui_ssp_free_run_final_pos值和自由滚动量
free_run_momentum值,
free_run_momentum= m->height/3;
g_gui_ssp_free_run_final_pos= (m->n_items * item_height) - m->heigh;
然后启动定时器,通过不断计算m->menu_offset_y值和刷屏实现菜单往回滑动
m->menu_offset_y的计算方法如下:
m->menu_offset_y=g_gui_ssp_free_run_final_pos+free_run_momentum*REDUCE_COEFFICIENT
最终菜单往回滑动到最后一个item与按键栏对齐
示意图如下:
4:列表菜单无高亮的实现:在之前触摸项目中,列表菜单的高亮条是一直存在的,每次滑动落笔时,就会高了到落笔那个item,自由滑动过程中,当高亮item滑出列表菜单显示范围2,滑动停止后,又会高亮到显示第一个或者最后一个item上,这种也会给人一种闪烁感,于是去掉列表滑动时的item高亮,只有在按住菜单item不放或者只单击item时才出现高亮。
刷新列表时,是否绘制高亮,用一个全局变态控制。
按住菜单item高亮的实现方式:落笔时,启动一个200ms定时器,在200ms时间到,定时器回调函数中判断,当前pen状态还在点击非move状态,重新刷新列表,并绘制高亮。
单击高亮的实现方式:单击提笔时,在相应单击操作之前重新刷新列表,并绘制高亮,在相应单击操作。
这种无高亮会带来一个问题,操作按键栏选项时,不知道当前操作的是哪个item,于是就将原来的选项操作分成两部分,一部分仍放在选项操作里面,即对所有item都有效的操作,比如删除全部,标记多个等,一部分通过长按item操作里面,即对单个item的操作,比如删除,重命名等。按照这种方式,如果每个模块都去更改菜单树,那代价太大,更改的地方太多,于是这种操作统一在滑动模块中处理,不管是点击选项还是长按item,都相应原来的选项操作,但是添加一个变量,记录当前操作是点击选项还是长按item,各应用模块只需要在原来的选项操作函数里面,通过获得该变量的值去对菜单的item进行hide或者unhide操作,就可以实现长按item和点击选项,进入不同的菜单,进行不同的操作,而不需要添加或更改原来的函数接口。