單元格合並一、單元格合並。 (1)我們可以使用Jtable的三個方法:getCellRect(),columnAtPoint(),and rowAtPoint()。第一個方法返回一個單元格的邊界(Rectangle類),第二、三個方法分別返回屏幕指定位置的列和行。為了實現單元格合並,我們需要重載(overwrite)這三個方法。 (2)另外我們需要找出渲染Jtable的ComponentUI對象,並且修改它以達到我們的目的。 (3)創建新的類記錄單元格合並情況的數據模型,它要包涵一個方法來取得單元格的所跨越的列數。另外,為了使用Jtable畫(paint)起來更容易些,我們需要一個方法來確定指定單元格是否被其它單元格所覆蓋,被哪個單元格覆蓋。我們將這兩種方法都集成在接口Cmap里:
現在我們開始重載上面提及過的三個方法。由於我們目前只關注於跨列單元格的合並,方法rowAtPoint()就不用重載了。然而,方法columnAtPoint()就必須重載了,我們會使用Jtable自身的方法來取得指定單元格的列值,並且計算出覆蓋該單元格的可視單元格列值(如果該單元格本來就是可視的,則返回自身列值)。在單元格合並后,在合並區域內只有一個跨越多列的可視單元格,其它被覆蓋的單元格則不會再被渲染。當使用getCellRect()方法取得被覆蓋的單元格的大小時,都返回覆蓋該單元格的可視單元格的大小。
現在剩下的就只有創建一個表格的渲染對象了。不同的用戶接口管理器(user interface managers)使用不同的類來畫表格。我們會繼承子類 javax.swing.plaf.basic.BasicTableUI,並且重載其方法 paintComponent。 在組件(component)畫在屏幕上之前,它已經被初始化和設定好了,所以我們能使用其內部的屬性 table 和 rendererPane。屬性 table 就是將要被畫在屏幕的表格,rendererPane 是用於將單元格畫在表格中的特殊對象。使用RendererPane的目的是打破單元格和表格的直接依賴關系,並且防止當一個單元格被修改時重畫整個表。 BasicTableUI的方法getClipBounds是用於找出表格的哪一部分將會被畫出來,所以我們首先要知道那些行是可視的,我們可以使用Jtable 的rowAtPoint方法。我們可以使用Rectangle類的intersects方法來確定這些行中的所有單元格是否將會被畫在屏幕上。在我們畫任何一個單元格前,我們必須檢查一下當前單元格是否可視,如果該單元格是被其它單元格所覆蓋的,就將覆蓋它的單元格畫出來。 根據單元格是否正在被編輯,單元格將會被方法getCellEditor或getCellRenderer所返回的對象畫出來。如果你查看一下BasicTableUI的源代碼,你就會發現所以單元格會先被BasicTableUI調用table.prepareRenderer畫(drawn)出來,然后再被BasicTableUI調用rendererPane.paintComponent來渲染(paint)。我們會采用同樣的方法。
今天下載了關於java swing的一個開源項目包tame,不過由於完成年代久遠(98年),很多類在新的jdk1.4或jdk1.5上已經會報錯。例如AttributiveCellTableModel類的setDataVector方法便需要改為:
public void setDataVector(Vector newData, Vector columnNames)
{ super.setDataVector(newData, columnNames); cellAtt = new DefaultCellAttribute(dataVector.size(),columnIdentifiers.size()); } 有心重整tame,先記一筆。
①java swing基於MVC架構,或者說是Model-driven結構。以jtable為例,它的特有GUI-State Model是
TableColumnModel(JTable是面向列的,它基於每一列進行繪制和編輯。分別是列繪制器TableCellRenderer和列編輯器TableCellEditor);它的共有GUI-State Model是Selection Model(和jlist、jtree等共用)。除了GUI-State Model,還有決定顯示在控件中的內容的Application-data model,jtable的Application-data model是TableModel。實現方式是先定義接口TableModel,再定義抽象類AbstractTableModel實現這個接口,然后由DefaultTableModel實現抽象類。不過一般來說用戶需要自己擴展AbstractTableModel實現它的幾個方法來獲取和設定值。
②以jtable為例,它並未提供實現單元格合並的方法。所以我們需要重載它的三個方法(getCellRect:獲取單元格的邊界,columnAtPoint和rowAtPoint:分別返回屏幕指定位置的列和行
)。
③現在我們需要自己繪制jtable,所以要用到Graphics類。另大部分的swing components 並不是直接由paint()方法來渲染(render),而是使用ComponentUI對象來完成渲染的。所以我們需要找出渲染Jtable的ComponentUI對象(BasicTableUI),並且修改它(重載paint()方法)以達到我們的目的。
④現在開始具體實現,tame先定義了4個接口(CellAttribute、ColoredCell、CellFont、CellSpan),用DefaultCellAttribute類實現了這四個接口,包含相關表格的基本屬性(顏色、字體、合並單元格的屬性等),這個類將每個cell定義為一個三維數組int[][][] span,並且都初始化為1。繼承自DefaultTableModel的AttributiveCellTableModel負責初始化table,至此並無特異之處。當需要合並單元格時。監聽按鈕將調用DefaultCellAttribute的combine方法把被覆蓋的單元格的三維數組int[][][] span設置為小於1,這樣在繪制的時候就可以判斷哪單元格可見,哪些單元格不可見了。接着通過重載jtable的三個方法得到cell的邊界以及行和列的位置,重繪表格的時候通過每個cell返回的結果(邊界、位置、是否可見)等循環繪制。
發表於 2006-04-05 09:04
沉思的狗
2010-05-01 11:11
JTABLE單元格合並(轉)最近,我為了做一個管理系統,需要用到合並JTable的單元格。查找了很多資料,終於簡單的實現了。現在把代碼共享出來,希望對大家有用。 本程序主要實現行的合並,列的合並大家可以根據下面的代碼修改。 CMap.java : package com; public interface CMap { /** CTUI.java : package com; import javax.swing.table.*; public class CTUI extends BasicTableUI { private void paintCell(int row, int column, Graphics g, Rectangle area) { Color c = g.getColor();
area.setBounds(area.x + horizontalMargin / 2, area.y + verticalMargin/ 2,
area.width - horizontalMargin,
area.height- verticalMargin);
if (table.isEditing() && table.getEditingRow() == row&& table.getEditingColumn() == column) { CTable.java :
package com;
import javax.swing.*; import javax.swing.table.*; import java.awt.*; public class CTable extends JTable { public CTable(CMap cmp, TableModel tbl) { public Rectangle getCellRect(int row, int column, boolean includeSpacing) { CMap1.java : /****************************************************************************************** CMap1對CMap地實現 span( ) 表示合並的單元格的列,返回的是合並的格數。 visibleCell() 表示要渲染的格。返回的渲染的開始格的行。 本程序的table是16行10列,合並的單元格是第一列和最后一列(最后一列是第10列)每兩個行。 *******************************************************************************************/ package com; import javax.swing.*; class CMap1 implements CMap {
public
int visibleCell(int row, int column) {
if( ( ( row >= 0 ) && ( row < 2 ) ) && ( column == 0 || column == 9 ) ) return 0; if( ( ( row >= 2 ) && ( row < 4 ) ) && ( column == 0 || column == 9 ) ) return 2; if( ( ( row >= 4 ) && ( row < 6 ) ) && ( column == 0 || column == 9 ) ) return 4; if( ( ( row >= 6 ) && ( row < 8 ) ) && ( column == 0 || column == 9 ) ) return 6; if( ( ( row >= 8 ) && ( row < 10 ) ) && ( column == 0 || column == 9 ) ) return 8; if( ( ( row >= 10 ) && ( row < 12 ) ) && ( column == 0 || column == 9 ) ) return 10; if( ( ( row >= 12 ) && ( row < 14 ) ) && ( column == 0 || column == 9 ) ) return 12; if( ( ( row >= 14 ) && ( row < 16 ) ) && ( column == 0 || column == 9 ) ) return 14; System.out.println( ">>>row = " + row + "column = " + column ); return row; } } 下面的程序進行測試。 package com; import javax.swing.*; public class CTest { CMap m = new CMap1(); |