JTable 本身不支援單元格合併,但可透過自訂 TableModel、TableUI 並覆寫 getCellRect()、columnAtPoint()、rowAtPoint() 三個方法實現跨列合併。本文提供核心介面 CMap、擴充 CTable 類別與自訂 CTUI 渲染器的完整架構說明。
package com.neuri.ctable;
public interface CMap {
/** 取得指定儲存格跨越的列數 */
int span(int row, int column);
/** 取得覆蓋指定儲存格的可視儲存格列索引;若本身可視則回傳自身列索引 */
int visibleCell(int row, int column);
}
package com.neuri.ctable;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
public class CTable extends JTable {
public CMap map;
public CTable(CMap cmp, TableModel tbl) {
super(tbl);
map = cmp;
setUI(new CTUI()); // 設定自訂 UI
}
@Override
public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
if (map == null) return super.getCellRect(row, column, includeSpacing);
int visCol = map.visibleCell(row, column);
Rectangle rect = super.getCellRect(row, visCol, includeSpacing);
// 若跨列,累加後續列寬
if (map.span(row, visCol) != 1) {
for (int i = 1; i < map.span(row, visCol); i++) {
rect.width += getColumnModel().getColumn(visCol + i).getWidth();
}
}
return rect;
}
@Override
public int columnAtPoint(Point p) {
int col = super.columnAtPoint(p);
if (col < 0) return col;
int row = super.rowAtPoint(p);
return map.visibleCell(row, col);
}
}
繼承 javax.swing.plaf.basic.BasicTableUI,覆寫 paintComponent(Graphics g),利用 rendererPane 只繪製可視儲存格,略過被合併覆蓋的儲存格。
// 實作 CMap,定義合併規則
CMap myMap = new CMap() {
@Override public int span(int row, int col) {
if (row == 0 && col == 0) return 3; // 第一儲存格跨 3 列
return 1;
}
@Override public int visibleCell(int row, int col) {
if (row == 0 && col > 0 && col < 3) return 0; // 第 1~2 列被第 0 列覆蓋
return col;
}
};
TableModel model = new DefaultTableModel(data, columnNames);
CTable table = new CTable(myMap, model);
注意事項:
rowAtPoint() 並擴充 CMap 介面。JIDE Grids、SwingX)以節省開發時間。