java - JComboBox 并不总是将值保存到 TableModel

标签 java swing jtable jcombobox

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

用户完成 JTable 编辑后,可以单击 JButton“保存”。在此示例中,它将仅将 TableModel 的值打印到控制台。

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

我认为我遗漏了一些东西,但我不知道问题出在哪里。

对于 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 here 解释的解决方案的问题

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 类,在方法 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;
    }

}

使用列表未进行任何更改的控制台输出

John --> Kim
Kim --> Peter
Peter --> John

关于java - JComboBox 并不总是将值保存到 TableModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54036283/

相关文章:

java - 重绘(),重新验证(),无效()。如何使用它们?

java - 如何自定义 JFrame 的标题栏?

java - 在类之间共享变量

java - 如何从同一 Activity 的 Fragment 中调用 public void

java - 在更新程序中引用文件(Java)

java - 我如何在java中清除我的框架屏幕?

java - 按命中次数对二维数组进行排序

java - 当我进入 JFrame 时如何对 JTable 的列进行排序?

java - Weka模型在android中读取错误

java - 中央认证服务(CAS)。关于 POST 请求的警告