JComboBox как Jtable CellEditor с переопределением stopCellEditing изменяет неправильную ячейку таблицы
У меня есть пользовательский JTable с пользовательским TableModel, использующий JComboBox в качестве редактора ячеек. ComboBox также имеет пользовательскую ComboBoxModel. Модель ComboBox содержит несколько полей, которые будут использоваться для обновления данных, стоящих за JTable, и последующего обновления базы данных.
Ниже приведен простой пример, показывающий проблему, с которой я сталкиваюсь. Действия по воспроизведению:
- Нажмите на ячейку
- Выберите значение в раскрывающемся списке ComboBox
- Нажмите на другую ячейку
- Нажмите обратно на первую выбранную ячейку
Вторая ячейка получит значение из первой.
Почему это происходит? Почему модель ComboBox изменяется до того, как StopCellEditing существует?
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestComboCellEditor {
public static void main(String[] args) {
TestComboCellEditor test = new TestComboCellEditor();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create and add a tabbed pane to the frame
JTabbedPane tabbedPane = new JTabbedPane();
frame.getContentPane().add(tabbedPane);
//create a table and add it to a scroll pane in a new tab
final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
JScrollPane scrollPane = new JScrollPane(table);
tabbedPane.addTab("test", scrollPane);
// create a simple JComboBox and set is as table cell editor on column A
Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
final JComboBox comboBox = new JComboBox(comboElements);
comboBox.setEditable(true);
table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox) {
@Override
public boolean stopCellEditing() {
if (comboBox.isEditable()) {
DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
String selectedItem = (String) comboModel.getSelectedItem();
int selectedIndex = comboModel.getIndexOf(selectedItem);
if (!(selectedIndex == -1)) {
// the selected item exists as an Option inside the ComboBox
DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
tableModel.setValueAt(selectedItem, selectedRow, selectedColumn);
} else if (selectedItem != null) {
// missing code - adding new info to a custom JComboBox model and to alter info inside a custom table model
}
}
return super.stopCellEditing();
}
});
// pack and show frame
frame.pack();
frame.setVisible(true);
}
}
2 ответа
Вот подход, который сохраняет весь код в редакторе:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TestComboCellEditor {
public static void main(String[] args) {
TestComboCellEditor test = new TestComboCellEditor();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create and add a tabbed pane to the frame
JTabbedPane tabbedPane = new JTabbedPane();
frame.getContentPane().add(tabbedPane);
//create a table and add it to a scroll pane in a new tab
final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
JScrollPane scrollPane = new JScrollPane(table);
tabbedPane.addTab("test", scrollPane);
// create a simple JComboBox and set is as table cell editor on column A
Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
final JComboBox comboBox = new JComboBox(comboElements);
comboBox.setEditable(true);
table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox)
{
private Object originalValue;
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
originalValue = value;
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
@Override
public boolean stopCellEditing()
{
JComboBox comboBox = (JComboBox)getComponent();
DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
Object editingValue = getCellEditorValue();
// Needed because your TableModel is empty
if (editingValue == null)
return super.stopCellEditing();
int selectedIndex = comboModel.getIndexOf(editingValue);
// Selecting item from model
if (! (selectedIndex == -1))
return super.stopCellEditing();
// Confirm addition of new value
int result = JOptionPane.showConfirmDialog(
comboBox.getParent(),
"Add (" + editingValue + ") to table?",
"Update Model",
JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION)
{
comboBox.addItem(editingValue);
return super.stopCellEditing();
}
else
{
comboBox.removeItem(editingValue);
comboBox.setSelectedItem(originalValue);
return false;
}
}
});
// pack and show frame
frame.pack();
frame.setVisible(true);
}
}
Хорошо, я сделал некоторые изменения, и я думаю, что что-то работает. Если это не лучшая практика, и вы получили лучшую реализацию, пожалуйста, оставьте ответ.
Изменить: не следуйте примеру ниже! Следуя комментариям Клеопатры, это неправильная реализация. Я оставляю это здесь, чтобы вы знали, как этого не делать.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestComboCellEditor {
public static void main(String[] args) {
TestComboCellEditor test = new TestComboCellEditor();
test.go();
}
public void go() {
//create the frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create and add a tabbed pane to the frame
JTabbedPane tabbedPane = new JTabbedPane();
frame.getContentPane().add(tabbedPane);
//create a table and add it to a scroll pane in a new tab
final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
JScrollPane scrollPane = new JScrollPane(table);
tabbedPane.addTab("test", scrollPane);
// create a simple JComboBox and set is as table cell editor on column A
Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
final JComboBox comboBox = new JComboBox(comboElements);
comboBox.setEditable(true);
table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox) {
@Override
public boolean stopCellEditing() {
if (comboBox.isEditable()) {
DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
String selectedItem = (String) comboModel.getSelectedItem();
int selectedIndex = comboModel.getIndexOf(selectedItem);
if (!(selectedIndex == -1)) {
comboBox.actionPerformed(new ActionEvent(this, selectedIndex, "blabla"));
} else if (selectedItem != null) {
// missing code - adding new info to a custom JComboBox model and to alter info inside a custom table model
}
}
return super.stopCellEditing();
}
});
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// the selected item exists as an Option inside the ComboBox
if (e.getActionCommand().equals("blabla")) {
DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
String selectedItem = (String) comboModel.getSelectedItem();
DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
tableModel.setValueAt(selectedItem, selectedRow, selectedColumn);
}
}
});
// pack and show frame
frame.pack();
frame.setVisible(true);
}
}