简体   繁体   English

如何在 JTable 中的级联 JCombobox 选项中进行选择?

[英]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);
        }
    }
}
  • Rather than create a new JComboBox each time method getTableCellEditorComponent is called, I initially create all three JComboBox es and return the relevant one.我不是在每次调用getTableCellEditorComponent方法时都创建一个新的JComboBox ,而是首先创建所有三个JComboBox并返回相关的一个。
  • You don't need to override method getCellEditorValue because the superclass method (in class DefaultCellEditor ) will return the correct value.您不需要重写方法getCellEditorValue因为超类方法(在 class DefaultCellEditor中)将返回正确的值。
  • Once the user changes the value (and closes the table cell editor), method 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.
  • Because I change the 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的首选大小。

EDIT编辑

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM