swing JTable使用

例子

实现下图效果:

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.*;

public class TableHeaderCenterExample extends JFrame {

    protected DefaultTableModel tableModel;

    public TableHeaderCenterExample() {
        super("表头居中的JTable示例");
        initUI();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(700, 400);
        setLocationRelativeTo(null);
    }

    private void initUI() {
        // JTable采用MVC设计模式,表头、数据封装在 DefaultTableModel 中
        // 定义表格列名
        String[] columnNames = {"文件名", "状态", "操作"};
        // 创建表格模型(最后一个参数表示不可编辑)
        tableModel = new DefaultTableModel(columnNames, 0) {
            @Override
            public boolean isCellEditable(int row, int column) {
                // 只有删除按钮列可编辑(否则按钮无法处理事件)
                return column == 2;
            }

            // 设置列数据类型,确保按钮能正确渲染
//            @Override
//            public Class<?> getColumnClass(int column) {
//                return (column == 2) ? JButton.class : String.class;
//            }
        };

        // 2. 创建表格
        // JTable table = new JTable(model);
        JTable table = new JTable();
        table.setModel(tableModel);

        //设置列宽度,必须调用 table.setModel(tableModel) 后才能执行
        // 获取列模型
        TableColumnModel columnModel = table.getColumnModel();

        // 方式1:固定列宽(最常用)
        columnModel.getColumn(0).setPreferredWidth(800); // 文件名:200像素
//        columnModel.getColumn(1).setPreferredWidth(50);  // 大小:80像素
        TableColumn column2 = columnModel.getColumn(1);
        column2.setPreferredWidth(220);  // 首选宽度
        column2.setMinWidth(120);       // 最小宽度(不能再小)
        column2.setMaxWidth(120);       // 最大宽度(不能再大)

//        columnModel.getColumn(2).setPreferredWidth(50); // 类型:120像素
        // 方式2:设置最小/最大宽度(限制拉伸范围)
        TableColumn column3 = columnModel.getColumn(2);
        column3.setPreferredWidth(100);  // 首选宽度
        column3.setMinWidth(100);       // 最小宽度(不能再小)
        column3.setMaxWidth(100);       // 最大宽度(不能再大)

        // 设置单元格居中显示
        // 创建居中渲染器
        DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
        headerRenderer.setHorizontalAlignment(SwingConstants.CENTER); // 水平居中
        headerRenderer.setVerticalAlignment(SwingConstants.CENTER);   // 垂直居中
        column2.setCellRenderer(headerRenderer);

        // 为第三列设置按钮渲染器和编辑器
        column3.setCellRenderer(new ButtonRenderer());
        // 点击表格时,会触发表格的编辑事件,而要想触发渲染的按钮的事件,还需要通过修改表格的默认编辑器来实现
        column3.setCellEditor(
                new ButtonEditor(new JCheckBox())
        );

        // 添加示例数据
        addTableRow("document.txt", "已处理");
        addTableRow("image.png", "未处理");
        addTableRow("data.csv", "处理中");
        addTableRow("archive.zip", "已处理");

        // 5. 添加到滚动面板
        JScrollPane scrollPane = new JScrollPane(table);
        getContentPane().add(scrollPane);
    }
    public void addTableRow(String file, String status) {
        // 每行数据:文件名、状态、删除按钮(按钮文本会被渲染器忽略)
        tableModel.addRow(new Object[]{
                file,
                status,
                "移除"
        });
    }

    // 按钮渲染器:负责在单元格中显示按钮
    private class ButtonRenderer extends JButton implements TableCellRenderer {
        public ButtonRenderer() {
            setOpaque(true);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                                                       boolean isSelected, boolean hasFocus,
                                                       int row, int column) {
            // 设置按钮文本
            setText((value == null) ? "删除" : value.toString());
            // 选中行时高亮按钮背景
//            setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
            return this;
        }
    }

    // 按钮编辑器:处理按钮点击事件
    private class ButtonEditor extends DefaultCellEditor {
        private JButton button;
        private String label;
        private boolean isPushed;
        private int selectedRow;

        public ButtonEditor(JCheckBox checkBox) {
            super(checkBox);
            button = new JButton();
            button.setOpaque(true);
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    fireEditingStopped(); // 停止编辑状态
                }
            });
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value,
                                                     boolean isSelected, int row, int column) {
            label = (value == null) ? "删除" : value.toString();
            button.setText(label);
            isPushed = true;
            selectedRow = row; // 记录当前行
            return button;
        }

        @Override
        public Object getCellEditorValue() {
            if (isPushed) {
                // 确认删除
                int confirm = JOptionPane.showConfirmDialog(
                        table,
                        "确定要移除 " + tableModel.getValueAt(selectedRow, 0) + " 吗?",
                        "警告",
                        JOptionPane.YES_NO_OPTION
                );
                if (confirm == JOptionPane.YES_OPTION) {
                    // 从表格模型中移除行
                    tableModel.removeRow(selectedRow);
                }
            }
            isPushed = false;
            return label;
        }

        @Override
        public boolean stopCellEditing() {
            isPushed = false;
            return super.stopCellEditing();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new TableHeaderCenterExample().setVisible(true);
        });
    }
}

注意

JTable 必须放在 JScrollPanel 中,才能显示表头

遍历数据

方式1:通过TableModel遍历(推荐)

private void traverseByModel(ActionEvent e) {
    StringBuilder sb = new StringBuilder();
    sb.append("通过模型遍历结果:\n");

    // 获取行数和列数
    int rowCount = tableModel.getRowCount();
    int colCount = tableModel.getColumnCount();

    // 遍历所有行和列
    for (int row = 0; row < rowCount; row++) {
        sb.append("第").append(row + 1).append("行:");
        for (int col = 0; col < colCount; col++) {
            // 获取单元格数据
            Object value = tableModel.getValueAt(row, col);
            sb.append(value).append(" | ");
        }
        sb.append("\n");
    }

    JOptionPane.showMessageDialog(this, sb.toString());
}

方式2:通过JTable组件遍历(受UI状态影响)

private void traverseByTable(ActionEvent e) {
    StringBuilder sb = new StringBuilder();
    sb.append("通过表格组件遍历结果:\n");

    // 获取行数和列数
    int rowCount = dataTable.getRowCount();
    int colCount = dataTable.getColumnCount();

    // 遍历所有行和列
    for (int row = 0; row < rowCount; row++) {
        sb.append("第").append(row + 1).append("行:");
        for (int col = 0; col < colCount; col++) {
            // 获取单元格数据(注意:这里的row是视图行号,可能与模型行号不同)
            Object value = dataTable.getValueAt(row, col);
            sb.append(value).append(" | ");
        }
        sb.append("\n");
    }

    JOptionPane.showMessageDialog(this, sb.toString());
}

清空数据

tableModel.setRowCount(0);

修改值

将第1行第2列的单元格修改为 转换成功:

tableModel.setValueAt("转换成功",0, 1);

原文出处:/show_1GW1cu0JEpH3.html