写这篇文章,主要是因为我们的“下午茶时间”(项目内部的一种交流方式)激烈地讨论了此类功能的多种实现,我感觉非常有意思。大家可以参考一下。
我先简单地描述一下功能要求,要求设计一种数据结构,支持网格控件(图形化)实现矩形区域的合并效果(Merged)。非矩形区域的合并不在支持范围之内。
大家可能都在使用各种各样的网格控件,最出名的可能是FlexGrid。不过大家可以也思考思考,如果是你的话,你会如何设计呢?
有一个最基本的方法,每一个网格CELL中预置一个变量:MergeID。我们姑且将这种方法称之为"MergeID法"。此方法规则比较简单:没有合并的CELL的MergeID都是0,相邻矩形区域的合并CELLs的MergeID相同,且不为0。将0特殊处理,是为了加快处理速度,毕竟,大多数都是没有合并的CELL。
1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 1
如上面的网格,则左上角六个CELL合并,右下角四个CELL合并。
这种方法的最大问题在于,空间占有比较浪费,特别是对那些从没有合并的CELL。另外,对于合并格的描述不清晰,整个网格中有多少合并格也不能简单获取,必须经过遍历才知道。
针对上面的分析,提出第二种方案,我们称之为"MergeRange List",这种方法的最大优势在于将所有合并的矩形区域统一管理起来,形成一个列表。网格控件在访问Merge属性的时候,再到Range列表中查找,如果找到,则属于合并格,否则为非合并格。
这个方式在FlexGrid就被使用。显然这种方法比较成熟。不过这种方式在管理对象的时候,不容易对应。需要经过映射。这在需要大量访问网格CELL对象的时候,会产生一些效率浪费。
熟悉HTML的人,大概还知道HTML的表示方式,在网格上通过rowspan和colspan来定义。在有合并的CELL的左上角上,定义其向右和向下的合并范围。通过这个范围定义,我们也可以确定合并区域。这种方式的定义简单。不过实现起来,其实有一些问题。
我们上面讨论几种方案的时候,忽略了一个问题,那就是我们一般的网格结构,很可能是一个CELL对应一个对象。那样的话,我们可以有一个新的方案,这个方案和第一个MergeID方案联系在一起,但是我们不需要额外的MergeID属性,而是直接使用Object的对象指针。只需要判断CELL周围的对象是否相等,就可以知道其合并范围。合并区域的所有CELL的对象相同。这样,在取数据的时候,不需要进行额外映射。而在网格控件绘制的时候,才需要合并区域查找。效率应该可以满足。我们就将这种方法叫"Ojbect/MergeID"吧。
总结列举一下四种方法:
-
MergeID
-
MergeRange List
-
Span
-
Object/MergeID
这四种方法各有利弊。其实在我们设计过程中,应该抓住一个基本点,要的是空间还是效率,还是两者都要。不同的要求,使用不同的设计。