[英]How to do a selection in cascade of JCombobox choices in a JTable?
I have a JTable with numbers 1,2,3 as 1st column and the number as text in the 2nd column which can be chosen with a JCombobox.我有一个 JTable,数字 1,2,3 作为第一列,数字作为第二列中的文本,可以使用 JCombobox 进行选择。 For example 1 can be represented as "1", ONE, FIRST or ONCE in the 2nd column.例如,1 可以在第 2 列中表示为“1”、ONE、FIRST 或 ONCE。 When I make a choice, all the comboboxes of the rows below have to be updated in cascade with the text of the same nature.当我做出选择时,下面行的所有组合框都必须使用相同性质的文本进行级联更新。 So if I choose ONCE, the comboboxes of the rows below should be updated to TWICE, THRICE.因此,如果我选择 ONCE,则下面各行的组合框应更新为 TWICE、THRICE。 If I choose FIRST, the comboboxes of the rows below should be updated to SECOND, THIRD.如果我选择 FIRST,下面各行的组合框应更新为 SECOND、THIRD。 And so on..等等..
At first it looks like it's working but whenever I click somewhere else on the JTable, the combobox is updated with the value of the last row.起初它看起来像是在工作,但每当我点击 JTable 上的其他位置时,combobox 就会更新为最后一行的值。 For example, if I choose ONCE in the 1st row, at first it will update the other rows to TWICE and THRICE.例如,如果我在第一行选择 ONCE,首先它会将其他行更新为 TWICE 和 THRICE。 Then if I click on any row, the combobox selection will be updated to THRICE on the 1st row.然后,如果我单击任何一行,combobox 选择将在第一行更新为 THRICE。 Next time I click, the 2nd row is updated to THRICE.下次单击时,第二行将更新为 THRICE。
What am I doing wrong here?我在这里做错了什么?
The combobox cell editor: combobox 单元格编辑器:
public class NumberChoiceEditor extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private String numberAsText;
private static final String[][] NUMBERS = { { "1", "ONE", "FIRST", "ONCE" }, { "2", "TWO", "SECOND", "TWICE" }, { "3", "THREE", "THIRD", "THRICE" } };
public NumberChoiceEditor() {
super(new JComboBox<>());
}
@Override
public Object getCellEditorValue() {
return numberAsText;
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
if (value instanceof String) {
numberAsText = (String) value;
}
JComboBox<String> numberTextChoice = new JComboBox<>(NUMBERS[row]);
numberTextChoice.setSelectedItem(numberAsText);
numberTextChoice.addActionListener(e -> {
numberAsText = (String) numberTextChoice.getSelectedItem();
int nextRow = row + 1;
if (nextRow < NUMBERS.length) {
String numberText = (String) table.getValueAt(nextRow, 1);
JComboBox<String> nextRowChoices = (JComboBox<String>) getTableCellEditorComponent(table, numberText, isSelected, nextRow, column);
nextRowChoices.setSelectedIndex(numberTextChoice.getSelectedIndex());
table.setValueAt(nextRowChoices.getSelectedItem(), nextRow, 1);
}
});
return numberTextChoice;
}
}
The main class with the frame:主要class配车架:
public class NumberTable {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTable table = new JTable(new Object[][] { { 1, "1" }, { 2, "2" }, { 3, "3" } }, new Object[] { "Number", "Number Text" });
table.getColumn("Number Text").setCellEditor(new NumberChoiceEditor());
table.setRowHeight(25);
JFrame frame = new JFrame("Number Table");
frame.add(new JScrollPane(table));
frame.setLocationRelativeTo(null);
frame.setSize(600, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
I would use a TableModelListener .我会使用TableModelListener 。 When the value is changed via the JComboBox
editor, I would adjust the TableModel
accordingly.当通过JComboBox
编辑器更改值时,我会相应地调整TableModel
。
(Notes after the code.) (代码后的注释。)
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class NumTable implements Runnable, TableModelListener {
private boolean adjusting;
private JFrame frame;
private JTable table;
@Override
public void run() {
createGui();
}
@Override
public void tableChanged(TableModelEvent event) {
if (!adjusting) {
adjusting = true;
int col = event.getColumn();
if (col == 1) {
NumChoiceEd editor = (NumChoiceEd) table.getColumnModel().getColumn(1).getCellEditor();
int row = event.getFirstRow();
JComboBox<String> combo = editor.getCombo(row);
if (combo != null) {
int ndx = combo.getSelectedIndex();
if (ndx >= 0) {
int rows = table.getRowCount();
for (int i = 0; i < rows; i++) {
combo = editor.getCombo(i);
String val = combo.getModel().getElementAt(ndx);
table.setValueAt(val, i, col);
}
}
}
}
adjusting = false;
}
}
private void createGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JScrollPane createTable() {
Object[][] data = new Object[][]{{1, "1"}, {2, "2"}, {3, "3"}};
Object[] columnNames = new Object[]{"Number", "Number Text"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
model.addTableModelListener(this);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnModel tcm = table.getColumnModel();
TableColumn column = tcm.getColumn(1);
column.setCellEditor(new NumChoiceEd());
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
public static void main(String[] args) {
EventQueue.invokeLater(new NumTable());
}
}
class NumChoiceEd extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private JComboBox<String> oneCombo;
private JComboBox<String> twoCombo;
private JComboBox<String> threeCombo;
public NumChoiceEd() {
super(new JComboBox<>());
oneCombo = new JComboBox<>(new String[]{"1", "ONE", "FIRST", "ONCE"});
twoCombo = new JComboBox<>(new String[]{"2", "TWO", "SECOND", "TWICE"});
threeCombo = new JComboBox<>(new String[]{"3", "THREE", "THIRD", "THRICE"});
}
public JComboBox<String> getCombo(int row) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return null;
}
}
@Override
public Component getTableCellEditorComponent(final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column) {
if (column == 1) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
else {
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}
JComboBox
each time method getTableCellEditorComponent
is called, I initially create all three JComboBox
es and return the relevant one.我不是在每次调用getTableCellEditorComponent
方法时都创建一个新的JComboBox
,而是首先创建所有三个JComboBox
并返回相关的一个。getCellEditorValue
because the superclass method (in class DefaultCellEditor
) will return the correct value.您不需要重写方法getCellEditorValue
因为超类方法(在 class DefaultCellEditor
中)将返回正确的值。tableChanged
is invoked.一旦用户更改了值(并关闭了表格单元格编辑器),就会调用方法tableChanged
。 In that method, I get the index of the value that was selected from the JComboBox
and then I go through all the rows in the JTable
and get the value at that index in the JComboBox
for each row and set the JTable
value to that value. In that method, I get the index of the value that was selected from the JComboBox
and then I go through all the rows in the JTable
and get the value at that index in the JComboBox
for each row and set the JTable
value to that value.TableModel
in method tableChanged
, that will cause the method to be called again.因为我在方法tableChanged
中更改了TableModel
,这将导致该方法再次被调用。 In order to prevent the recursive call, I use the adjusting
flag.为了防止递归调用,我使用了adjusting
标志。 In the below screen capture, I have selected a value from the JComboBox
but I have not yet closed the editor.在下面的屏幕截图中,我从JComboBox
中选择了一个值,但我还没有关闭编辑器。 If I navigate to a different cell in the JTable
that will close the editor and then all the displayed data will change.如果我导航到JTable
中的另一个单元格,它将关闭编辑器,然后所有显示的数据都会改变。 Note that if you navigate to column Number Text in a different row, you may not see the change since that will immediately open the JComboBox
editor for the cell that you navigated to.请注意,如果您导航到不同行中的Number Text列,您可能看不到更改,因为这将立即打开您导航到的单元格的JComboBox
编辑器。
After I close the editor, the table looks as in the below screen capture.关闭编辑器后,表格如下面的屏幕截图所示。
Note that there is a lot of blank space in the above screen captures since the default dimensions of JTable
are quite large but the space required to display the data (in this case) is small.请注意,上述屏幕截图中有很多空白区域,因为JTable
的默认尺寸非常大,但显示数据所需的空间(在这种情况下)很小。 One way to make the JTable
smaller (in this case) would be to change the preferred size of the JScrollPane
.使JTable
更小(在这种情况下)的一种方法是更改JScrollPane
的首选大小。
In response to the question you asked in your comment , namely针对您在评论中提出的问题,即
is it possible to update on the combobox value change是否可以更新 combobox 值更改
Yes, it is possible.对的,这是可能的。 You add an ActionListener
to each JComboBox
that simply calls method stopCellEditing .您向每个JComboBox
添加一个ActionListener
,它只调用方法stopCellEditing 。 Here is the above code, modified to include the ActionListener
.这是上面的代码,修改为包含ActionListener
。 The only changes are in class NumChoiceEd
.唯一的变化是 class NumChoiceEd
。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class NumTable implements Runnable, TableModelListener {
private boolean adjusting;
private JFrame frame;
private JTable table;
@Override
public void run() {
createGui();
}
@Override
public void tableChanged(TableModelEvent event) {
if (!adjusting) {
adjusting = true;
int col = event.getColumn();
if (col == 1) {
NumChoiceEd editor = (NumChoiceEd) table.getColumnModel().getColumn(1).getCellEditor();
int row = event.getFirstRow();
JComboBox<String> combo = editor.getCombo(row);
if (combo != null) {
int ndx = combo.getSelectedIndex();
if (ndx >= 0) {
int rows = table.getRowCount();
for (int i = 0; i < rows; i++) {
combo = editor.getCombo(i);
String val = combo.getModel().getElementAt(ndx);
table.setValueAt(val, i, col);
}
}
}
}
adjusting = false;
}
}
private void createGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JScrollPane createTable() {
Object[][] data = new Object[][]{{1, "1"}, {2, "2"}, {3, "3"}};
Object[] columnNames = new Object[]{"Number", "Number Text"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
model.addTableModelListener(this);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnModel tcm = table.getColumnModel();
TableColumn column = tcm.getColumn(1);
column.setCellEditor(new NumChoiceEd());
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
public static void main(String[] args) {
EventQueue.invokeLater(new NumTable());
}
}
class NumChoiceEd extends DefaultCellEditor {
private static final long serialVersionUID = 1L;
private JComboBox<String> oneCombo;
private JComboBox<String> twoCombo;
private JComboBox<String> threeCombo;
public NumChoiceEd() {
super(new JComboBox<>());
ActionListener al = event -> NumChoiceEd.this.stopCellEditing(); // ADDED THIS LINE
oneCombo = new JComboBox<>(new String[]{"1", "ONE", "FIRST", "ONCE"});
oneCombo.addActionListener(al); // ADDED THIS LINE
twoCombo = new JComboBox<>(new String[]{"2", "TWO", "SECOND", "TWICE"});
twoCombo.addActionListener(al); // ADDED THIS LINE
threeCombo = new JComboBox<>(new String[]{"3", "THREE", "THIRD", "THRICE"});
threeCombo.addActionListener(al); // ADDED THIS LINE
}
public JComboBox<String> getCombo(int row) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return null;
}
}
@Override
public Component getTableCellEditorComponent(final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column) {
if (column == 1) {
switch (row) {
case 0:
return oneCombo;
case 1:
return twoCombo;
case 2:
return threeCombo;
default:
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
else {
return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.