12.8Java Swing 中的MVC

发布于:2025-06-19 ⋅ 阅读:(17) ⋅ 点赞:(0)

在 Java Swing 中,MVC 模式被广泛应用。例如,JTable、JList 等组件都采用了这种模式。通常:

  • 模型:实现特定的 Swing 模型接口(如 TableModel、ListModel)。
  • 视图:是 Swing 组件本身(如 JTable、JList)。
  • 控制器:通常隐含在组件内部,或由开发者实现为事件监听器。

JTableModel 是 Java Swing 中用于管理表格数据的核心接口,它是 MVC(Model-View-Controller)模式在表格组件中的具体实现。JTable 作为视图,负责显示数据;而 JTableModel 作为模型,负责存储和管理数据,并提供数据访问接口。

JTableModel 接口方法

JTableModel 接口定义了以下核心方法:

  1. 基本结构方法

    • int getRowCount():返回表格的行数
    • int getColumnCount():返回表格的列数
    • String getColumnName(int columnIndex):返回指定列的名称
    • Class<?> getColumnClass(int columnIndex):返回指定列的数据类型
  2. 数据访问方法

    • Object getValueAt(int rowIndex, int columnIndex):获取指定单元格的数据
    • void setValueAt(Object aValue, int rowIndex, int columnIndex):设置指定单元格的数据
  3. 可选方法

    • boolean isCellEditable(int rowIndex, int columnIndex):指定单元格是否可编辑

实现方式

JTableModel 有三种主要实现方式:

  1. DefaultTableModel

    • 最简单的实现,使用 Vector 存储数据和列名
    • 缺点:所有单元格数据类型被视为 Object,不支持类型安全
  2. AbstractTableModel

    • 抽象基类,提供了事件通知机制
    • 通常继承此类并实现必要的方法
  3. 自定义 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);

            // 创建自定义TableModel
            PersonTableModel model = new PersonTableModel();
            
            // 添加示例数据
            model.addPerson(new Person("张三", 25, false));
            model.addPerson(new Person("李四", 30, true));
            model.addPerson(new Person("王五", 22, false));

            // 创建表格并关联TableModel
            JTable 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);
    }

    // 返回表格行数
    @Override
    public int getRowCount() {
        return data.size();
    }

    // 返回表格列数
    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

    // 返回列名
    @Override
    public String getColumnName(int column) {
        return columnNames[column];
    }

    // 返回列数据类型
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnTypes[columnIndex];
    }

    // 返回单元格数据
    @Override
    public 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;
        }
    }

    // 设置单元格数据并使单元格可编辑
    @Override
    public 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);
    }

    // 设置单元格是否可编辑
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true; // 所有单元格都可编辑
    }
}    

JTableModel 关键特性

  1. 事件通知机制

    • AbstractTableModel 提供了事件通知方法:
      • fireTableDataChanged():整个表格数据已更改
      • fireTableStructureChanged():表格结构已更改
      • fireTableRowsInserted/Updated/Deleted():行数据更改
      • fireTableCellUpdated():单元格数据更改
  2. 列类型支持

    • 通过getColumnClass()方法返回列的数据类型
    • JTable 会根据列类型自动提供合适的渲染器和编辑器
  3. 单元格编辑

    • 通过isCellEditable()方法控制单元格是否可编辑
    • 通过setValueAt()方法处理编辑后的数据

网站公告

今日签到

点亮在社区的每一天
去签到