JComboBox 并不总是将值保存到 TableModel

JComboBox does not always save value to TableModel

我创建了一个 JTable 列表,其中包含人员列表。一个人总是可以包含另一个人(或者可能是他自己)。因此用户可以单击该列并出现 JComboBox。此 JComboBox 包含 JTable 的所有人员。

用户编辑完JTable后,可以点击JButton "Save"。在此示例中,它只会将 TableModel 的值打印到控制台。

问题: 最后的更改不是 "transfered" TableModel。
例如。用户为每一行选择人 Kim。对他来说看起来是正确的,但如果单击按钮,您将看到最后一行的 "Person" 列的值仍然是 John。 当用户单击另一列时,在第三列中更改人员后,然后单击 Button,输出正确。

我想我遗漏了什么,但我想不出问题出在哪里。

对于 JTable,我使用了本教程: https://www.codejava.net/java-se/swing/how-to-create-jcombobox-cell-editor-for-jtable

这是我的代码:

public class Person {
    private String name;
    private Person person;
    private String job;

    public Person(String name, String job) {
        this.name = name;
        this.job = job;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

}

public class PersonTable extends JTable {

    private PersonTableModel tableModel;

    public PersonTable(List<Person> listPerson) {
        tableModel = new PersonTableModel(listPerson);
        this.setModel(tableModel);
        this.setDefaultRenderer(Person.class, new PersonCellRenderer());
        this.setDefaultEditor(Person.class, new PersonCellEditor(listPerson));
    }

    public Set<Person> getAllPersons() {
        Set<Person> set = new HashSet<Person>();
        for (int i = 0; i < tableModel.getRowCount(); i++) {
            set.add(tableModel.getPerson(i));
        }
        return set;
    }

}

public class PersonCellEditor extends AbstractCellEditor 
        implements TableCellEditor, ActionListener {

    private Person Person;
    private List<Person> listPerson;

    public PersonCellEditor(List<Person> listPerson) {
        this.listPerson = listPerson;
    }

    @Override
    public Object getCellEditorValue() {
        return this.Person;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        if (value instanceof Person) {
            this.Person = (Person) value;
        }

        JComboBox<Person> comboPerson = new JComboBox<Person>();
        comboPerson.setRenderer(new PersonComboBoxRenderer());

        for (Person aPerson : listPerson) {
            comboPerson.addItem(aPerson);
        }

        comboPerson.setSelectedItem(Person);
        comboPerson.addActionListener(this);

         if (isSelected) {
             comboPerson.setBackground(table.getSelectionBackground());
         } else {
             comboPerson.setBackground(table.getSelectionForeground());
         }

        return comboPerson;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        JComboBox<Person> comboPerson = (JComboBox<Person>) event.getSource();
        this.Person = (Person) comboPerson.getSelectedItem();
    }

}

public class PersonCellRenderer extends DefaultTableCellRenderer {

     public Component getTableCellRendererComponent(JTable table, Object value, 
             boolean isSelected, boolean hasFocus, int row, int column) {
         if (value instanceof Person) {
             Person Person = (Person) value;
             setText(Person.getName());
         }

         if (isSelected) {
             setBackground(table.getSelectionBackground());
         } else {
             setBackground(table.getSelectionForeground());
         }

         return this;
     }

}

public class PersonTableModel extends AbstractTableModel {
    private String[] columnNames = {"No.", "Name", "Person", "Job"};
    private List<Person> listPerson = new ArrayList<>();

    public PersonTableModel(List<Person> listPerson) {
        this.listPerson.addAll(listPerson);
    }

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

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

    public Class getColumnClass(int column) {
        return getValueAt(0, column).getClass();
    }

    @Override
    public int getRowCount() {
        return listPerson.size();
    }

    @Override
    public void setValueAt(Object value, int rowIndex, int columnIndex) {
        Person person = listPerson.get(rowIndex);

        switch (columnIndex) {
        case 1:
                person.setName((String) value);
            break;
        case 2:
                person.setPerson((Person) value);
            break;
        case 3:
                person.setJob((String) value);
            break;
        }        
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Object returnValue = null;
        Person person = listPerson.get(rowIndex);

        switch (columnIndex) {
        case 0:
                returnValue = rowIndex + 1;
            break;
        case 1:
                returnValue = person.getName();
            break;
        case 2:
                returnValue = person.getPerson();
            break;
        case 3:
                returnValue = person.getJob();
            break;
        }

        return returnValue;
    }

    public Person getPerson(int row) {
        return listPerson.get(row);
    }

    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex > 0;
    }    

}

public class PersonComboBoxRenderer extends DefaultListCellRenderer {

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
            boolean cellHasFocus) {
        super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if(value instanceof Person) {
            Person p = (Person) value;
            setText(p.getName());
        }
        return this;
    }

}

public class JComboBoxTableCellEditorExample extends JFrame {

    public JComboBoxTableCellEditorExample() {
        super("JComboBox Cell Editor for JTable Demo");

        List<Person> listPerson = new ArrayList<>();
        Person p1 = new Person("John", "Developer");
        Person p2 = new Person("Kim", "Designer");
        Person p3 = new Person("Peter", "Manager");
        p1.setPerson(p2);
        p2.setPerson(p3);
        p3.setPerson(p1);
        listPerson.add(p1);
        listPerson.add(p2);
        listPerson.add(p3);

        PersonTable table = new PersonTable(listPerson);

        JScrollPane scrollpane = new JScrollPane(table);
        scrollpane.setPreferredSize(new Dimension(400, 200));

        add(scrollpane, BorderLayout.CENTER);

        JButton buttonSave = new JButton("Save");
        buttonSave.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                for (Person p : table.getAllPersons()) {
                    System.out.println(p.getName() + " --> " + p.getPerson().getName());
                }
            }
        });

        add(buttonSave, BorderLayout.PAGE_END);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new JComboBoxTableCellEditorExample();
            }
        });
    }

}

我已经测试了你的代码并解决了应用 camickr

解释的解决方案的问题

您遇到的问题由camickr 本人在entry 中解释。摘录一段

When editing a cell in a JTable the table doesn’t know when a user is finished editing the cell. Therefore it is the users responsibility to tell the table when to stop editing.

This is normally done by:

  • using the enter key
  • tabbing to the next cell
  • clicking on another cell with the mouse

如果您编辑第三行 Person [第 2 列] 并立即按下“保存”按钮,JTable 不会检测到您已完成对该单元格的编辑。这就是你获得

的原因
Kim --> Kim
John --> Kim
Peter --> John

camickr 提供的解决方案之一是当您检测到“保存”按钮已被按下时强制停止编辑。只需更改您的按钮声明

    JButton buttonSave = new JButton("Save");
    buttonSave.addActionListener(new ActionListener() {
                  
        @Override
        public void actionPerformed(ActionEvent e) {
            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
            }
        
            for (Person p : table.getAllPersons()) {
                System.out.println(p.getName() + " --> " + p.getPerson().getName());
            }
        }
    });

现在你应该得到想要的结果了。

请注意,如果您稍微更改 PersonTable class,将方法 getAllPersons 中的 Set 替换为 List,您将获得有序结果,从而提高控制台的可读性。

public class PersonTable extends JTable {

    private PersonTableModel tableModel;

    public PersonTable(List<Person> listPerson) {
        tableModel = new PersonTableModel(listPerson);
        setModel(tableModel);
        setDefaultRenderer(Person.class, new PersonCellRenderer());
        setDefaultEditor(Person.class, new PersonCellEditor(listPerson));
    }

    public List<Person> getAllPersons() {
        List<Person> set = new ArrayList<Person>();
        
        for (int i = 0; i < tableModel.getRowCount(); i++) {
            set.add(tableModel.getPerson(i));
        }
        
        return set;
    }

}

使用 List

未进行任何更改的控制台输出
John --> Kim
Kim --> Peter
Peter --> John