mavok
mavok

Reputation: 47

JComboBox does not always save value to TableModel

I have created a JTable with a list of Persons. A Person can always contain another Person (or maybe itself). So the user can click at the column and a JComboBox appears. This JComboBox contains all of the persons of the JTable.

After the user finished editing the JTable, he can click on the JButton "Save". In this example it will just print the values of the TableModel to console.

Problem: The last change is not "transfered" to the TableModel.
E.G. the user selects for every row the person Kim. For him it is looking right, but if you click the button, you will see for the last row the value of column "Person" is still John. When the user clicks at another column, after changing persons in third column, and then clicks the Button, the output is correct.

I think I am missing something, but I can't figure out where the problem is.

For the JTable I have used this tutorial: https://www.codejava.net/java-se/swing/how-to-create-jcombobox-cell-editor-for-jtable

Here is my code:

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();
            }
        });
    }

}

Upvotes: 0

Views: 82

Answers (1)

RubioRic
RubioRic

Reputation: 2468

I've tested your code and fixed the problem applying the solution explained by camickr here

The problem that you face is explained by camickr himself in this entry. Let me quote an excerpt

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

If you edit the third row Person [column 2] and inmediately push the "Save" button, the JTable doesn't detect that you have finished editing that cell. That's the reason that you obtain

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

One of the solution offered by camickr is forcing the stop of the editing when you detect that the "Save" button has been pushed. Just change your button declaration

    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());
            }
        }
    });

Now you should obtain the desired result.

Notice that if you change a bit your PersonTable class, replacing Set by List in method getAllPersons, you would obtain an ordered result improving the readability of the console.

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;
    }

}

Console output with no changes using List

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

Upvotes: 2

Related Questions