在 Java Swing 中,MVC 模式被廣泛應用。例如,JTable、JList 等組件都采用了這種模式。通常:
- 模型:實現特定的 Swing 模型接口(如 TableModel、ListModel)。
- 視圖:是 Swing 組件本身(如 JTable、JList)。
- 控制器:通常隱含在組件內部,或由開發者實現為事件監聽器。
JTableModel 是 Java Swing 中用于管理表格數據的核心接口,它是 MVC(Model-View-Controller)模式在表格組件中的具體實現。JTable 作為視圖,負責顯示數據;而 JTableModel 作為模型,負責存儲和管理數據,并提供數據訪問接口。
JTableModel 接口方法
JTableModel 接口定義了以下核心方法:
-
基本結構方法
int getRowCount()
:返回表格的行數int getColumnCount()
:返回表格的列數String getColumnName(int columnIndex)
:返回指定列的名稱Class<?> getColumnClass(int columnIndex)
:返回指定列的數據類型
-
數據訪問方法
Object getValueAt(int rowIndex, int columnIndex)
:獲取指定單元格的數據void setValueAt(Object aValue, int rowIndex, int columnIndex)
:設置指定單元格的數據
-
可選方法
boolean isCellEditable(int rowIndex, int columnIndex)
:指定單元格是否可編輯
實現方式
JTableModel 有三種主要實現方式:
-
DefaultTableModel
- 最簡單的實現,使用 Vector 存儲數據和列名
- 缺點:所有單元格數據類型被視為 Object,不支持類型安全
-
AbstractTableModel
- 抽象基類,提供了事件通知機制
- 通常繼承此類并實現必要的方法
-
自定義 TableModel
- 完全自定義實現,適用于復雜數據結構和特殊需求
案例:自定義 TableModel 實現
下面通過一個案例展示如何實現自定義 TableModel:
Main.java
import javax.swing.*;
import java.awt.*;public class Main {public static void main(String[] args) {SwingUtilities.invokeLater(() -> {// 創建主窗口JFrame frame = new JFrame("人員信息管理");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(500, 300);frame.setLocationRelativeTo(null);// 創建自定義TableModelPersonTableModel model = new PersonTableModel();// 添加示例數據model.addPerson(new Person("張三", 25, false));model.addPerson(new Person("李四", 30, true));model.addPerson(new Person("王五", 22, false));// 創建表格并關聯TableModelJTable table = new JTable(model);// 添加表格到滾動面板JScrollPane scrollPane = new JScrollPane(table);// 添加按鈕面板JPanel buttonPanel = new JPanel();JButton addButton = new JButton("添加");JButton deleteButton = new JButton("刪除");// 添加按鈕事件處理addButton.addActionListener(e -> {String name = JOptionPane.showInputDialog(frame, "請輸入姓名:");if (name != null && !name.isEmpty()) {String ageStr = JOptionPane.showInputDialog(frame, "請輸入年齡:");if (ageStr != null && !ageStr.isEmpty()) {try {int age = Integer.parseInt(ageStr);String marriedStr = JOptionPane.showInputDialog(frame, "是否已婚(true/false):");boolean married = Boolean.parseBoolean(marriedStr);model.addPerson(new Person(name, age, married));} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(frame, "年齡必須是數字!");}}}});deleteButton.addActionListener(e -> {int selectedRow = table.getSelectedRow();if (selectedRow != -1) {model.deletePerson(selectedRow);} else {JOptionPane.showMessageDialog(frame, "請先選擇一行!");}});buttonPanel.add(addButton);buttonPanel.add(deleteButton);// 添加組件到窗口frame.getContentPane().add(scrollPane, BorderLayout.CENTER);frame.getContentPane().add(buttonPanel, BorderLayout.SOUTH);// 顯示窗口frame.setVisible(true);});}
}
PersonTableModel.java?
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.List;// 人員類,存儲表格中的一行數據
class Person {private String name;private int age;private boolean married;public Person(String name, int age, boolean married) {this.name = name;this.age = age;this.married = married;}public String getName() { return name; }public int getAge() { return age; }public boolean isMarried() { return married; }public void setName(String name) { this.name = name; }public void setAge(int age) { this.age = age; }public void setMarried(boolean married) { this.married = married; }
}// 自定義TableModel實現
public class PersonTableModel extends AbstractTableModel {private static final long serialVersionUID = 1L;// 列名數組private final String[] columnNames = {"姓名", "年齡", "已婚"};// 列數據類型數組private final Class<?>[] columnTypes = {String.class, Integer.class, Boolean.class};// 數據列表private final List<Person> data = new ArrayList<>();// 添加人員數據public void addPerson(Person person) {data.add(person);// 通知表格數據已插入fireTableRowsInserted(data.size() - 1, data.size() - 1);}// 更新人員數據public void updatePerson(int row, Person person) {data.set(row, person);// 通知表格數據已更新fireTableRowsUpdated(row, row);}// 刪除人員數據public void deletePerson(int row) {data.remove(row);// 通知表格數據已刪除fireTableRowsDeleted(row, row);}// 獲取指定行的人員數據public Person getPerson(int row) {return data.get(row);}// 返回表格行數@Overridepublic int getRowCount() {return data.size();}// 返回表格列數@Overridepublic int getColumnCount() {return columnNames.length;}// 返回列名@Overridepublic String getColumnName(int column) {return columnNames[column];}// 返回列數據類型@Overridepublic Class<?> getColumnClass(int columnIndex) {return columnTypes[columnIndex];}// 返回單元格數據@Overridepublic Object getValueAt(int rowIndex, int columnIndex) {Person person = data.get(rowIndex);switch (columnIndex) {case 0: return person.getName();case 1: return person.getAge();case 2: return person.isMarried();default: return null;}}// 設置單元格數據并使單元格可編輯@Overridepublic void setValueAt(Object value, int rowIndex, int columnIndex) {Person person = data.get(rowIndex);switch (columnIndex) {case 0: person.setName((String) value);break;case 1: person.setAge((Integer) value);break;case 2: person.setMarried((Boolean) value);break;}// 通知表格單元格數據已更新fireTableCellUpdated(rowIndex, columnIndex);}// 設置單元格是否可編輯@Overridepublic boolean isCellEditable(int rowIndex, int columnIndex) {return true; // 所有單元格都可編輯}
}
JTableModel 關鍵特性
-
事件通知機制
- AbstractTableModel 提供了事件通知方法:
fireTableDataChanged()
:整個表格數據已更改fireTableStructureChanged()
:表格結構已更改fireTableRowsInserted/Updated/Deleted()
:行數據更改fireTableCellUpdated()
:單元格數據更改
- AbstractTableModel 提供了事件通知方法:
-
列類型支持
- 通過
getColumnClass()
方法返回列的數據類型 - JTable 會根據列類型自動提供合適的渲染器和編輯器
- 通過
-
單元格編輯
- 通過
isCellEditable()
方法控制單元格是否可編輯 - 通過
setValueAt()
方法處理編輯后的數據
- 通過