Reputation: 47
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
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