首页 > 代码库 > UITableView分组冻结的研究与实现浅析

UITableView分组冻结的研究与实现浅析

功能描述

    当我们使用iphone手机通讯录,手机qq的时候。有一个细节大家可能已经忽略,即“分组”控件在向屏幕上方滑动时,超过屏幕的高度,就冻结到屏幕最上端。方便用户确定当前浏览的是哪个分组。当下面的分组上来时候,又会当前冻结的窗口顶上去,它自己又实现冻结!这就是分组冻结功能。

    这篇两年前都应该写的博客,直到今天才写出来,才发现当初的设计和思路已经模糊,只能看代码来分析了。所以这篇文章只是回忆一下当初踊跃的思路。

    为了能够说明白这个东西,再举一个例子。在使用excel的时候,冻结窗口的功能:固定了第一行(通常是标题行)方便用户对照标题和数据。下面这个gif图片展示了冻结分组的效果。注意“测试”这个分组的效果,当cell移动的时候,它并没有移动,而是定在哪儿了!




2012年,笔者在Symbian平台上实现了分组冻结的功能(纯自绘的);以iOS平台中tableview为例,在使用它的使用,我们必定要实现下面两个函数,分别用来设置section和cell的view。其中section就具备冻结功能:
tableView: viewForHeaderInSection:
tableView: cellForRowAtIndexPath:

笔者还不太清楚iOS 中的tableview是如何实现分组冻结的,因为iOS已经做的很完善了。

下面详细说一下如何在【自绘控件】环境下,实现分组冻结功能。

1. 传统的tableview中,section和cell是一并绘制的,现将section和cell的绘制分开

    既然section能够冻结,那么section在UI层次中的顺序关系就是:section高于cell,这样section冻结的时候,section才能正常显示;或者说将section的控件最后add到view的控件数组中。下面我们看看在绘制控件的时候的先后层次:


上面这个图是飞信塞班版本中UI的层次结构,主要说明UI的绘制是分层的。

2. 设定一个Y值上限,通常是tableview的上沿的Y值

设定一个Y值,冻结分组的时候,最上端是多少,这也是主要的参数了。例如下面的代码

iFreezeEngine = FreezeEngine::NewL(this);
iFreezeEngine->SetFreezeLine(Area().iTl.iY);//topLeft.Y

3. 建立一个FreezeEngine的类和CCanFreezeControl类

● FreezeEngine类用于:创建一系列的“可冻结控件”(CCanFreezeControl),并对“可冻结控件”进行设置。CCanFreezeControl跟普通的控件相比,多了“冻结状态”。
那么view、FreezeEngine和“可冻结控件”是怎么个关系呢。看看下面的类图:

tableview由FreezeEngine和多个cell组成。FreezeEngine管理这多个CCanFreezeControl。cell和CCanFreezeControl都继承自Control基类。如果把FreezeEngine去掉就是正常的view和control的关系了。

FreezeEngine类中两个重要属性是:冻结线,可冻结控件数组。

TInt iFreezeLine;
RPointerArray<CCanFreezeControl> iCanFreezeControlArray;

● CCanFreezeControl类

该类中有三个重要参数,根据这三个参数可以计算出:何时需要冻结?何时从冻结的地方回来。

TInt iFreezeOffset;          //是一个负数,在这个区间 [-iGroupItemHeight,0]
TInt iFreezeAccumulate; //是一个倍数,iGroupItemHeight的倍数。
TInt iGroupItemHeight;    //一个分组的高度

还有一个最重要的函数TBool CCanFreezeControl::CalcFreezeOffset(const TRect & aSrcRect,TRect& aDesRect)负责计算,当分组接近冻结时候的计算,已经冻结时候的计算。

4. 可冻结控件的冻结状态

● “测试”分组【接近冻结】状态,即前面正好有一个iGroupItemHeight的举例,如下图:

因为当前分组接近冻结的时候,需要将上一个已经冻结的分组(未分组好友)顶上去!注意“未分组好友”上去,并非真正“测试”分组顶上去的,而是FreezeEngine的功劳。
● 【已冻结】分组状态,参考上图中“未分组好友”的分组,即【已冻结】状态。其实有时候回想【已冻结】的分组去哪儿了呢?其实很简单,被顶上去的分组就在iFreezeLine上面了。只是我们看不到了。另外一些很早已经冻结的窗口,仍是按照自己逻辑坐标绘制,在屏幕外面。

红色的线是冻结线(iFreezeLine)【我的家人】被顶上去,【我的同事】已经冻结,其他已经冻结的分组按照自己逻辑坐标正常显示到屏幕外面了。之所以这样是因为:正常的思维,接近冻结和接近解冻是非常关键的时刻,需要小心处理。

5. 还要实现分组控件的按键点击

由于冻结的分组扔需要响应事件,所以,当屏幕点击到“冻结分组”的时候,需要转换成分组实际的坐标进行匹配,才能确定是否响应这个点击。

最后,由于笔者正致力于iOS开发,这些UI基础功能,iOS系统已经做到极致了,所以。这篇博文只是来回忆一下symbian的代码。

UITableView分组冻结的研究与实现浅析