摘要:本文主要叙述如何通过建立数据库模型(单元格模型)。重载JTable的三个重要方法(getCellRect(),columnAtPoint(),rowAtPoint())和继承Jtable的渲染组件(BasicTableUI)来实现单元格的合并.
关键字:Jtable,合并单元格,重载
如何工程涉及到表格的话,就不得不使用Java Swing 中的JTable的合并单元格的实现。Jtable没有提供现成的合并单元格的方法,但是使用其所提供的方法仍然能做到.
对于表格中的单元格的合并,我们可以这样实现:待合并单元格应该是表格中紧密相连并处于长方形区域的多个单元格,合并实际上是将处于该长方形区域中的最左上方的单元格(我们暂之为合并单元格)边界扩大至整个长方形区域,而其他处于该区域的单元格则被隐藏起来,所有对隐藏单元格的访问都被传送到其对应的合并单元格中处理。因此我们需要一个数据模型来记录单元格的覆盖和隐藏情况,为了与常用的数据模型(TableMode)区分开,我们暂且称其位单元格模型(CellModel)
合并单元格跨多行多列,为了使Jtable了解合并单元格的大小,需要重载getCellRect(),该函数的作用是返回单元格的边界大小(Rectangle类),对于合并单元格我们需要在该函数内实现合并单元格边界的大小的计算。为了将被隐藏单元格的访问重定位到其对应的合并单元格中,我们需要重载columnAtPoint()和rowAtPoint(),它们分别返回指定位置所在的列和行值。对于被隐藏的单元格,返回的都是其对应合并单元格的列和行值。
另外,大部分的swing components并不是直接有paint()方法来渲染,而是使用ComponentUI对象来完成渲染,而JTable也是其中一员,所以我们需要找出渲染Jtable的ComponentUI对象,并且修改它以达到我们的目标.
1.单元格模型
要实现多行多列单元格合并需要多个类相互协作,直接写出来的话可能比较复杂。所以我们先讨论一下记录单元格合并情况的数据模型。该数据模型的设计概念如下;
表格的每一个单元格所在的位置需使用二维数据来标示,此外每一个单元格使用一个长度为2的数据(int)数组标示,数组第一个数据为对应单元格所跨的行数,第二个数值为对应单元格所跨越的列数,因此单元格模型采用一个三维数据记录信息(int[][][]spn)。当一个表格刚刚初始化,其所对应的单元格模型值为数组(1,1),表示每个单元格高一行,宽一列。当一个单元格合并多行多列该如何表示?
先比如有7行7列的表格,对于跨多行多列的单元格(我们暂且称之为合并单元格)所对应的单元格模型值为(所跨行数,所跨列数),如第2行第2列交汇处的单元格跨4行3列,所有其对应的单元格模型值为(4,3)。已被覆盖的单元格所对应的单元格模型值(1-当前单元格与合并单元格相差的行数,1-当前单元格与合并单元格相差的列数)。以第5行第4列交汇处单元格为例,它与合并单元格相差3行2列,它对应的单元格模型值为(1-3,1-2)=(-2,-1)。这样做的目的在于,对所有被覆盖的单元格取值,所返回的应该是其对应的合并单元格的值,那该如何定位所对应的合并单元格的位置了。这样我们采取偏离值计算的原因。以第5行第4列交汇处的单元格位例,其单元格模型对应的值为(-2,-1),用初始值(1,1)-(-2,-1)得到偏离值3行2列,就能定位到合并单元格(第5行-3 行,第4列-2列)=(第2列第2行)。这样的话,我们只需要在表格数据模型的getValueAt()和setValueAt()函数中作位置转换就能适应可合并单元格表格使用。另外,从图中可看出可是单元格对用的单元格模型值皆大于1,而被覆盖单元格对应的单元格模型值最少有一个小于1.因此我们能从单元格模型值来确定该单元格是否可视。
由于swing里没有单元格模型类可以直接使用,所有我们需要一个新的类来保存处理及单元格模型的数据,它包涵一个方法(int[] sapn(int row,int column))设置单元格的所跨越的行数,另外一个方法void setSpan(int[] span,int row,int column)设置单元格所跨域的行列数。另外,为了使用JTable画起来比较容易,我们需要一个方法(int visibleCell(int row,int column)来确定指定单元格是否被其他单元格所覆盖,被那个单元格覆盖。最后,我们需要合并 (void combine(int[] rows,int[] columns))及拆分(void split(int row,int column)函数实现单元格的合并以及拆分。我们将这五种方法都集成在接口CellModelInterface里。
当JTable表格中出现合并单元格时,其对应的数据模型(TableModel)中的getValueAt()和setValueAt()应实现隐藏单元格取值赋值操作的重定位,这就需要将数据模型(TableModel)要对其表格单元模型有所了解。最好的办法是将数据模型(TableModel)和单元格模型(CellModel)结合在一起,如;
2.JTable中的三个重要方法的实现
现在我们开始重载上面提及过的三个方法。为了实现方法rowAtPoint()和columnAtPoint()的重载,我们会使用JTable自身的方法(super.rowAtPoint()和super.columnAtPoint())来取得指定单元格的行列值.如何当前位置的单元格时被隐藏的就需要计算出覆盖该单元格的合并单元格的列值,否则直接返回自身方法取得的列值。在单元格合并后,在合并区域内只有一个跨行多行多列的可是单元格,其他被覆盖的单元格则不会被渲染。当使用getCellRec()方法取得被覆盖的单元格大小时,都返回覆盖该单元格的可视单元格的大小。
public Rectangle getCellRect(int row,int column,boolean includeSpacing)
对于合并单元格重新计算其边界大小,而对于隐藏单元格则返回其对应合并单元格边界值,对于一般的单元格直接返回其原来边界.
public int columnAtPoint(Point p)
如果当前单元格位隐藏,获取其对应合并单元格的列值,否则返回其原来的列值
public int rowAtPoint(Point p)
如果当前单元格为隐藏,获取其对应的单元格的行值,否则返回其原来的行值。