深度解析 JTable 的模型-視圖分離架構,透過繼承 AbstractTableModel 實現自訂資料模型,支援列類型自動識別(布林顯示為核取方塊)、指定列可編輯控制、資料變更通知與除錯輸出,打造生產級表格元件。
class MyTableModel extends AbstractTableModel {
// 欄位定義
final String[] columnNames = {"姓名", "性別", "學歷", "年齡", "是否已婚"};
// 初始資料(二維陣列)
final Object[][] data = {
{"張三", "男", "大學", 25, false},
{"李四", "男", "大學", 33, true},
{"王五", "男", "高中", 20, false},
{"趙倩", "女", "大專", 26, true},
{"周大", "男", "大學", 24, false}
};
// 必須實作的三大核心方法
public int getColumnCount() { return columnNames.length; }
public int getRowCount() { return data.length; }
public Object getValueAt(int row, int col) { return data[row][col]; }
// 進階功能覆寫
public String getColumnName(int col) { return columnNames[col]; }
// 關鍵:根據首行資料類型決定渲染器/編輯器(Boolean → JCheckBox)
public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); }
// 控制可編輯性:前兩列唯讀,其餘可編輯
public boolean isCellEditable(int row, int col) { return col >= 2; }
// 資料更新處理
public void setValueAt(Object value, int row, int col) {
System.out.println("更新: [" + row + "," + col + "] = " + value);
data[row][col] = value;
fireTableCellUpdated(row, col); // 通知視圖重繪該儲存格
printDebugData(); // 除錯輸出完整表格
}
private void printDebugData() {
for (int i = 0; i < getRowCount(); i++) {
System.out.print("行 " + i + ":");
for (int j = 0; j < getColumnCount(); j++) System.out.print(" " + data[i][j]);
System.out.println();
}
System.out.println("--------------------------");
}
}
| 方法 | 作用 | 不覆寫後果 |
|---|---|---|
getColumnClass() | 決定預設渲染器/編輯器 | 所有儲存格用 toString() 顯示,Boolean 顯示 "true/false" 而非核取方塊 |
isCellEditable() | 控制儲存格能否編輯 | 全部可編輯或全部不可編輯(預設 false) |
setValueAt() | 處理編輯提交、資料驗證、觸發事件 | 編輯無效果,資料不更新 |
fireTableCellUpdated() | 發送 TableModelEvent 通知視圖 | 視圖不重繪,顯示舊資料 |
public class MyTableDemo extends JFrame {
public MyTableDemo() {
super("Custom Table Model Demo");
MyTableModel model = new MyTableModel();
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack(); setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MyTableDemo());
}
}
setValueAt() 中加入正則/範圍檢查,失敗拋出例外或恢復舊值。table.setDefaultEditor(MyClass.class, new MyCellEditor())。TableRowSorter<MyTableModel>。setValueAt() 中呼叫 DAO 更新資料庫,失敗回滾。