模型修改后充满文件的 JTable 重复值 (AbstractTableModel)

JTable filled with files repeats value after model modification (AbstractTableModel)

我又在和 JTable 纠缠了,我很卡。

问题是:

我发现很多帖子都有类似的问题,但无法解决(尝试 cellRenderer、cellEditor、修改模型等)。下面我尽量把代码精简到最简单。我尝试了很多不同的东西,但我真的不明白发生了什么事的逻辑(我完整地阅读了文档,但显然我遗漏了一些东西)。

我也试过用DefaultTableModel做一个版本,但问题似乎是独立的。此外,我想保留 AbstractTableModel 因为其他 类 使用它。我认为模型中出现了问题,但我不知道是什么(在模型中也尝试了不同的火***)。

如果您看到错误并能解释其背后的逻辑,将不胜感激。

非常感谢。这是我认为不言自明的代码:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;


public class FillTableExample extends JFrame {

private JLabel label = new JLabel("File:");
private JButton selectButton = new JButton("Select");
private JPanel labelButtonPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
private Object rowDataTable[][] = {{"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""}};
private String[] columnNames = {"File", "Name"};
private CustomTableModel fileTableModel = new CustomTableModel(rowDataTable, columnNames);
private JTable fileTable = new JTable(fileTableModel);
private JScrollPane scrollPane = new JScrollPane(fileTable);
private FillTable fillTableActionListener = new FillTable(fileTableModel);
private JPanel container = new JPanel();
private JCheckBox changeCol = new JCheckBox("ChangeCol");



public FillTableExample(){


    selectButton.addActionListener(fillTableActionListener);
    labelButtonPane.add(label);
    labelButtonPane.add(selectButton);

    changeCol.addActionListener(new DisplayPane());

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setExtendedState(MAXIMIZED_BOTH);
    this.setMinimumSize(new Dimension(400,400));
    this.setResizable(true);
    container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
    container.add(labelButtonPane);
    container.add(changeCol);
    container.add(scrollPane);
    this.setContentPane(container);
    this.setVisible(true);
}


public class CustomTableModel extends AbstractTableModel {

    private Object rowData[][];
    private String[] columnNames;

    public CustomTableModel(Object data[][], String[] titles){
        rowData = data;
        columnNames = titles;
    }

    public int getColumnCount() {
        return columnNames.length;
    }

    public String getColumnName(int column) {
        return columnNames[column];
    }

    public int getRowCount() {
        return rowData.length;
    }

    public Object getValueAt(int row, int column) {
        return rowData[row][column];
    }

    public Class getColumnClass(int column) {
        return this.rowData[0][column].getClass();
    }


    public int getColumnIndex(String columnName){
        return Arrays.asList(columnNames).indexOf(columnName);
    }

    public void setValueAt(Object value, int row, int column) {
        rowData[row][column] = value;
       // fireTableDataChanged();
    }

    public boolean isCellEditable(int row, int column) {

        if(column == 0 && columnNames[0].equals("Sample Name"))
            return true;
        else
            return (column != 0);
    }
}

class DisplayPane implements ActionListener {


    public void actionPerformed(ActionEvent e){

        final String[] tmpContent1 = {"", "", ""};
        final String[] newColumnNames = {"File", "Name", "Extension"};

        for (int i = 0; i < rowDataTable.length; i++)
                rowDataTable[i] = tmpContent1;
        fileTableModel = new CustomTableModel(rowDataTable, newColumnNames);

        fileTable.setModel(fileTableModel);
        fillTableActionListener.setModel(fileTableModel);
        fileTableModel.fireTableStructureChanged();
        fileTableModel.fireTableDataChanged();
    }
}


class FillTable implements ActionListener {

    private CustomTableModel model;

    public FillTable(CustomTableModel model){

        this.model = model;
    }

    public void actionPerformed(ActionEvent e){

        final JFileChooser fc = new JFileChooser();
        int returnVal;

        fc.setMultiSelectionEnabled(true);
        returnVal = fc.showOpenDialog(FillTableExample.this);

        model.setValueAt("toto", 0, 0);
        //I tried this, but you can see that toto is duplicated when clicking on cell
        //((AbstractTableModel) fileTable.getModel()).fireTableCellUpdated(0, 0);
        model.fireTableDataChanged();
    }

    public void setModel(CustomTableModel model){
        this.model = model;
    }
}


class FileEditor extends DefaultCellEditor {

    public FileEditor(){
        super(new JTextField());
    }
    public Component getTableCellEditorComponent(JTable table, Object value,
              boolean isSelected, int row, int column) {

        JTextField editor = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);

        if(column == 0){
            if(value != "toto")
                editor.setText("");
            else
                editor.setText(value.toString());
        }

        return editor;
    }
}


public static void main(String[] args) {

    FillTableExample example = new FillTableExample();
    example.setVisible(true);
}
}

[******************编辑]

我在构造函数中添加了命令"fileTable.getColumnModel().getColumn(0).setCellEditor(new FileEditor());"。同样的错误

这是使用 DefaultTableModel 的代码(同样的错误):

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;


public class FillTableExample2 extends JFrame {

private JLabel label = new JLabel("File:");
private JButton selectButton = new JButton("Select");
private JPanel labelButtonPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
private Object rowDataTable[][] = {{"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""}};
private String[] columnNames = {"File", "Name"};
private DefaultTableModel fileTableModel = new DefaultTableModel(rowDataTable, columnNames);
private JTable fileTable = new JTable(fileTableModel);
private JScrollPane scrollPane = new JScrollPane(fileTable);
private FillTable fillTableActionListener = new FillTable(fileTableModel);
private JPanel container = new JPanel();
private JCheckBox changeCol = new JCheckBox("ChangeCol");



public FillTableExample2(){


    selectButton.addActionListener(fillTableActionListener);
    labelButtonPane.add(label);
    labelButtonPane.add(selectButton);

    changeCol.addActionListener(new DisplayPane());
    fileTable.getColumnModel().getColumn(0).setCellEditor(new FileEditor());

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setExtendedState(MAXIMIZED_BOTH);
    this.setMinimumSize(new Dimension(400,400));
    this.setResizable(true);
    container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
    container.add(labelButtonPane);
    container.add(changeCol);
    container.add(scrollPane);
    this.setContentPane(container);
    this.setVisible(true);
}


class DisplayPane implements ActionListener {


    public void actionPerformed(ActionEvent e){
        fileTableModel.addColumn("Extension");
    }
}


class FillTable implements ActionListener {

    private DefaultTableModel model;

    public FillTable(DefaultTableModel model){

        this.model = model;
    }

    public void actionPerformed(ActionEvent e){

        final JFileChooser fc = new JFileChooser();
        int returnVal;

        fc.setMultiSelectionEnabled(true);
        returnVal = fc.showOpenDialog(FillTableExample2.this);

        model.setValueAt("toto", 0, 0);
    }
}


class FileEditor extends DefaultCellEditor {

    public FileEditor(){
        super(new JTextField());
    }
    public Component getTableCellEditorComponent(JTable table, Object value,
              boolean isSelected, int row, int column) {

        JTextField editor = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);

        if(value != "toto")
            editor.setText("");
        else
            editor.setText(value.toString());

        return editor;
    }
}


public static void main(String[] args) {

    FillTableExample example = new FillTableExample();
    example.setVisible(true);
}
}

请找到固定的 class :

class DisplayPane implements ActionListener {

        public void actionPerformed(ActionEvent e) {

            final String[] newColumnNames = { "File", "Name", "Extension" };

            for (int i = 0; i < rowDataTable.length; i++){
                rowDataTable[i] =new String[] { "", "", "" }; // See below
            }
            fileTableModel = new CustomTableModel(rowDataTable, newColumnNames);

            fileTable.setModel(fileTableModel);
            fillTableActionListener.setModel(fileTableModel);
            fileTableModel.fireTableStructureChanged();
            fileTableModel.fireTableDataChanged();
        }
    }

该错误是由于对每一行使用相同的字符串数组造成的。 一个好的做法是从模型中触发事件。

非常感谢!问题是我在每一行中都复制了相同的 String[]。因此,修改一个字符串会在整个列中传播。声明一个新的 String[] 确保每行中有不同的内容。

在模型中放入一个 fireTableDataChanged() 并从 class 中删除 fileTableModel.fireTableStructureChanged() 和 fileTableModel.fireTableDataChanged() 就可以了。

完整的操作代码如下:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;


public class FillTableExample extends JFrame {

private JLabel label = new JLabel("File:");
private JButton selectButton = new JButton("Select");
private JPanel labelButtonPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
private Object rowDataTable[][] = {{"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""}};
private String[] columnNames = {"File", "Name"};
private CustomTableModel fileTableModel = new CustomTableModel(rowDataTable, columnNames);
private JTable fileTable = new JTable(fileTableModel);
private JScrollPane scrollPane = new JScrollPane(fileTable);
private FillTable fillTableActionListener = new FillTable(fileTableModel);
private JPanel container = new JPanel();
private JCheckBox changeCol = new JCheckBox("ChangeCol");



public FillTableExample(){


    selectButton.addActionListener(fillTableActionListener);
    labelButtonPane.add(label);
    labelButtonPane.add(selectButton);

    changeCol.addActionListener(new DisplayPane());

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setExtendedState(MAXIMIZED_BOTH);
    this.setMinimumSize(new Dimension(400,400));
    this.setResizable(true);
    container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
    container.add(labelButtonPane);
    container.add(changeCol);
    container.add(scrollPane);
    this.setContentPane(container);
    this.setVisible(true);
}


public class CustomTableModel extends AbstractTableModel {

    private Object rowData[][];
    private String[] columnNames;

    public CustomTableModel(Object data[][], String[] titles){
        rowData = data;
        columnNames = titles;
    }

    public int getColumnCount() {
        return columnNames.length;
    }

    public String getColumnName(int column) {
        return columnNames[column];
    }

    public int getRowCount() {
        return rowData.length;
    }

    public Object getValueAt(int row, int column) {
        return rowData[row][column];
    }

    public Class getColumnClass(int column) {
        return this.rowData[0][column].getClass();
    }


    public int getColumnIndex(String columnName){
        return Arrays.asList(columnNames).indexOf(columnName);
    }

    public void setValueAt(Object value, int row, int column) {
        rowData[row][column] = value;
       fireTableDataChanged();
    }

    public boolean isCellEditable(int row, int column) {

        if(column == 0 && columnNames[0].equals("Sample Name"))
            return true;
        else
            return (column != 0);
    }
}

class DisplayPane implements ActionListener {


    public void actionPerformed(ActionEvent e){

        final String[] newColumnNames = {"File", "Name", "Extension"};

        for (int i = 0; i < rowDataTable.length; i++)
                rowDataTable[i] = new String[]{"", "", ""};
        fileTableModel = new CustomTableModel(rowDataTable, newColumnNames);

        fileTable.setModel(fileTableModel);
        fillTableActionListener.setModel(fileTableModel);
    }
}


class FillTable implements ActionListener {

    private CustomTableModel model;

    public FillTable(CustomTableModel model){

        this.model = model;
    }

    public void actionPerformed(ActionEvent e){

        final JFileChooser fc = new JFileChooser();
        int returnVal;

        fc.setMultiSelectionEnabled(true);
        returnVal = fc.showOpenDialog(FillTableExample.this);

        model.setValueAt("toto", 0, 0);
    }

    public void setModel(CustomTableModel model){
        this.model = model;
    }
}


public static void main(String[] args) {

    FillTableExample example = new FillTableExample();
    example.setVisible(true);
}
}

[**********编辑]

你是对的,使用 DefaultTableModel 代码更轻便和直接。这是更正后的示例 2:

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;


public class FillTableExample2 extends JFrame {

private JLabel label = new JLabel("File:");
private JButton selectButton = new JButton("Select");
private JPanel labelButtonPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
private Object rowDataTable[][] = {{"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""},
        {"", ""}, {"", ""}, {"", ""}, {"", ""}};
private String[] columnNames = {"File", "Name"};
private DefaultTableModel fileTableModel = new DefaultTableModel(rowDataTable, columnNames);
private JTable fileTable = new JTable(fileTableModel);
private JScrollPane scrollPane = new JScrollPane(fileTable);
private FillTable fillTableActionListener = new FillTable(fileTableModel);
private JPanel container = new JPanel();
private JCheckBox changeCol = new JCheckBox("ChangeCol");



public FillTableExample2(){


    selectButton.addActionListener(fillTableActionListener);
    labelButtonPane.add(label);
    labelButtonPane.add(selectButton);

    changeCol.addActionListener(new DisplayPane());

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setExtendedState(MAXIMIZED_BOTH);
    this.setMinimumSize(new Dimension(400,400));
    this.setResizable(true);
    container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
    container.add(labelButtonPane);
    container.add(changeCol);
    container.add(scrollPane);
    this.setContentPane(container);
    this.setVisible(true);
}


class DisplayPane implements ActionListener {


    public void actionPerformed(ActionEvent e){
        fileTableModel.addColumn("Extension");
    }
}


class FillTable implements ActionListener {

    private DefaultTableModel model;

    public FillTable(DefaultTableModel model){

        this.model = model;
    }

    public void actionPerformed(ActionEvent e){

        final JFileChooser fc = new JFileChooser();
        int returnVal;

        fc.setMultiSelectionEnabled(true);
        returnVal = fc.showOpenDialog(FillTableExample2.this);

        model.setValueAt("toto", 0, 0);
    }
}

public static void main(String[] args) {

    FillTableExample2 example = new FillTableExample2();
    example.setVisible(true);
}
}