I have a GUI with a column of JComboBoxes, where each one is unique. I really don't understand what I'm doing so the latest in an extrodinarily long sequence of roadblocks is that when a box is edited, it doesn't display the newly-selected value. If the box is selected again, the value in the cell updates to what was previously chosen, so presumably it's just a matter of the table/renderer not updating.

I suspect I probably need to register one listener per combo box somewhere, but I wouldn't know which type of listener on which class.

Here's the SSCCE illustrating as concisely as I could manage the design as it stands:

import java.awt.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

@SuppressWarnings("serial")
public class ComboBoxTableGUI extends JFrame {

    public List<SampleCollection> masterList;

    public static void main(String[] args) {
        new ComboBoxTableGUI();
    }

    public ComboBoxTableGUI() {
        // create a collection of 10 collections, each with 1-10 Strings
        masterList = new ArrayList<SampleCollection>();
        Random rnd = new Random();
        for (int i = 0; i < 10; i++) {
            SampleCollection sc = new SampleCollection("Option " + i + ":");
            int choices = rnd.nextInt(9) + 1;
            for (int j = 0; j < choices; j++) {
                sc.choices.add("Choice " + j);
            }
            sc.selectedChoice = sc.choices.get(0);
            masterList.add(sc);
        }

        this.setSize(500, 500);
        this.setLocationRelativeTo(null);

        TableModel optionTableModel = new OptionTableModel();
        JTableWithCustomEditors optionsTable = new JTableWithCustomEditors(optionTableModel);

        this.add(optionsTable);

        TableColumn optionCol = optionsTable.getColumnModel().getColumn(1);
        optionCol.setCellRenderer(new CustomComboBoxRenderer());

        List<TableCellEditor> editors = new ArrayList<TableCellEditor>();
        for (SampleCollection collection : masterList) {
            JComboBox<String> cb = new JComboBox<String>();
            for (String choice : collection.choices) {
                cb.addItem(choice);
            }
            DefaultCellEditor editor = new DefaultCellEditor(cb);
            editors.add(editor);
        }
        optionsTable.editors = editors;

        this.setVisible(true);
    }

    public class SampleCollection {
        public String name, selectedChoice;
        public List<String> choices;

        public SampleCollection(String name) {
            this.name = name;
            this.choices = new ArrayList<String>();
        }
    }

    public class JTableWithCustomEditors extends JTable {
        public List<TableCellEditor> editors;

        public JTableWithCustomEditors(TableModel model) {
            super(model); // lol
        }

        @Override
        public TableCellEditor getCellEditor(int row, int column) {
            if (column == 1)
                return editors.get(row);
            else
                return super.getCellEditor(row, column);
        }
    }

    class CustomComboBoxRenderer extends JComboBox<String> implements
            TableCellRenderer {
        public CustomComboBoxRenderer() {
            setBorder(new EmptyBorder(0, 0, 0, 0));
        }

        public Component getTableCellRendererComponent(JTable table,
                Object value, boolean isSelected, boolean hasFocus, int row,
                int column) {

            removeAllItems();
            addItem(((SampleCollection) value).selectedChoice);

            this.setSelectedIndex(0);
            return this;
        }
    }

    public class OptionTableModel implements TableModel {
        private List<TableModelListener> listeners;

        public OptionTableModel() {
            listeners = new ArrayList<TableModelListener>();
        }

        @Override
        public void addTableModelListener(TableModelListener l) {
            listeners.add(l);
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            if (columnIndex == 1)
                return SampleCollection.class;
            else
                return String.class;
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public String getColumnName(int columnIndex) {
            if (columnIndex == 0)
                return "Name";
            else
                return "Selection";
        }

        @Override
        public int getRowCount() {
            if (masterList == null)
                return 0;
            else
                return masterList.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            if (columnIndex == 0)
                return masterList.get(rowIndex).name;
            else
                return masterList.get(rowIndex);// also tried .choices
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            if (columnIndex == 1)
                return true;
            else
                return false;
        }

        @Override
        public void removeTableModelListener(TableModelListener l) {
            listeners.remove(l);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            // the values are grabbed when the user clicks an OK button, right
            // now I just need to get it rendering
        }
    }
}
有帮助吗?

解决方案

Your TableModel implementation is incorrect.

First of all the data should be stored in the model not as an instance variable in your class.

The setValueAt(...) method should update the data in your List and should then invoke fireTableCellUpdated(...) which will tell the table the data has changed so the cell can be repainted.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top