简体   繁体   中英

JTable and custom JComboBox

I want to implement a custom JComboBox as an CellEditor for a JTable. I took the original Java Example and modified it by implementing a Sports class

Please run the source code, click on the JComboBox and you will note that the selected item is not selected anymore when the ComboBox opens. I want to keep the renderered item in the combobox selection.

Original: http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#combobox

public class Sport {
    private Integer id;
    private String name;

    public Sport(String name){
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

     public String toString() {
        return this.name;
    }
}



public class TableRenderDemo extends JPanel {
    private boolean DEBUG = false;

public TableRenderDemo() {
    super(new GridLayout(1,0));

    JTable table = new JTable(new MyTableModel());
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));
    table.setFillsViewportHeight(true);

    //Create the scroll pane and add the table to it.
    JScrollPane scrollPane = new JScrollPane(table);

    //Set up column sizes.
    initColumnSizes(table);

    //Fiddle with the Sport column's cell editors/renderers.
    setUpSportColumn(table, table.getColumnModel().getColumn(2));

    //Add the scroll pane to this panel.
    add(scrollPane);
}

/*
 * This method picks good column sizes.
 * If all column heads are wider than the column's cells'
 * contents, then you can just use column.sizeWidthToFit().
 */
private void initColumnSizes(JTable table) {
    MyTableModel model = (MyTableModel)table.getModel();
    TableColumn column = null;
    Component comp = null;
    int headerWidth = 0;
    int cellWidth = 0;
    Object[] longValues = model.longValues;
    TableCellRenderer headerRenderer =
        table.getTableHeader().getDefaultRenderer();

    for (int i = 0; i < 5; i++) {
        column = table.getColumnModel().getColumn(i);

        comp = headerRenderer.getTableCellRendererComponent(
                             null, column.getHeaderValue(),
                             false, false, 0, 0);
        headerWidth = comp.getPreferredSize().width;

        comp = table.getDefaultRenderer(model.getColumnClass(i)).
                         getTableCellRendererComponent(
                             table, longValues[i],
                             false, false, 0, i);
        cellWidth = comp.getPreferredSize().width;

        if (DEBUG) {
            System.out.println("Initializing width of column "
                               + i + ". "
                               + "headerWidth = " + headerWidth
                               + "; cellWidth = " + cellWidth);
        }

        column.setPreferredWidth(Math.max(headerWidth, cellWidth));
    }
}

public void setUpSportColumn(JTable table,
                             TableColumn sportColumn) {
    //Set up the editor for the sport cells.
    JComboBox comboBox = new JComboBox();
    comboBox.addItem("Snowboarding");
    comboBox.addItem("Rowing");
    comboBox.addItem("Knitting");
    comboBox.addItem("Speed reading");
    comboBox.addItem("Pool");
    comboBox.addItem("None of the above");
    sportColumn.setCellEditor(new DefaultCellEditor(comboBox));

    //Set up tool tips for the sport cells.
    DefaultTableCellRenderer renderer =
            new DefaultTableCellRenderer();
    renderer.setToolTipText("Click for combo box");
    sportColumn.setCellRenderer(renderer);
}

class MyTableModel extends AbstractTableModel {
    private String[] columnNames = {"First Name",
                                    "Last Name",
                                    "Sport",
                                    "# of Years",
                                    "Vegetarian"};
    private Object[][] data = {
    {"Kathy", "Smith",new Sport("Snowboarding"),
            new Integer(5), new Boolean(false)},
    {"John", "Doe",
     new Sport("Rowing"), new Integer(3), new Boolean(true)},
    {"Sue", "Black",
     new Sport("Knitting"), new Integer(2), new Boolean(false)},
    {"Jane", "White",
     new Sport("Speed reading"), new Integer(20), new Boolean(true)},
    {"Joe", "Brown",
     new Sport("Pool"), new Integer(10), new Boolean(false)}
    };

    public final Object[] longValues = {"Jane", "Kathy",
                                        "None of the above",
                                        new Integer(20), Boolean.TRUE};

    public int getColumnCount() {
        return columnNames.length;
    }

    public int getRowCount() {
        return data.length;
    }

    public String getColumnName(int col) {
        return columnNames[col];
    }

    public Object getValueAt(int row, int col) {
        return data[row][col];
    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     */
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        if (col < 2) {
            return false;
        } else {
            return true;
        }
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    public void setValueAt(Object value, int row, int col) {
        if (DEBUG) {
            System.out.println("Setting value at " + row + "," + col
                               + " to " + value
                               + " (an instance of "
                               + value.getClass() + ")");
        }

        data[row][col] = value;
        fireTableCellUpdated(row, col);

        if (DEBUG) {
            System.out.println("New value of data:");
            printDebugData();
        }
    }

    private void printDebugData() {
        int numRows = getRowCount();
        int numCols = getColumnCount();

        for (int i=0; i < numRows; i++) {
            System.out.print("    row " + i + ":");
            for (int j=0; j < numCols; j++) {
                System.out.print("  " + data[i][j]);
            }
            System.out.println();
        }
        System.out.println("--------------------------");
    }
}

/**
 * Create the GUI and show it.  For thread safety,
 * this method should be invoked from the
 * event-dispatching thread.
 */
private static void createAndShowGUI() {
    //Create and set up the window.
    JFrame frame = new JFrame("TableRenderDemo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create and set up the content pane.
    TableRenderDemo newContentPane = new TableRenderDemo();
    newContentPane.setOpaque(true); //content panes must be opaque
    frame.setContentPane(newContentPane);

    //Display the window.
    frame.pack();
    frame.setVisible(true);
}

public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
}
}

Ok I included the following, but still the same problem:

    public void setUpSportColumn(JTable table,
                                 TableColumn sportColumn) {
        //Set up the editor for the sport cells.
        JComboBox<Sport> comboBox = new JComboBox();
        comboBox.addItem(new Sport("Snowboarding"));
        comboBox.addItem(new Sport("Rowing"));
        comboBox.addItem(new Sport("Knitting"));
        comboBox.addItem(new Sport("Speed reading"));
        comboBox.addItem(new Sport("Pool"));
        comboBox.addItem(new Sport("None of the above"));
        sportColumn.setCellEditor(new DefaultCellEditor(comboBox));

        //Set up tool tips for the sport cells.
        DefaultTableCellRenderer renderer =
                new DefaultTableCellRenderer();
        renderer.setToolTipText("Click for combo box");
        sportColumn.setCellRenderer(renderer);
//        sportColumn.setCellEditor(new MyCellEditor());
    }

Your model contains instances of Sport class but the JCombobox contains strings. That's why set selection can't find proper value.

Try to add sports like this

 comboBox.addItem(new Sport("Rowing"));

And either override toString() method of Sport class to return the name (easier way). Or add a custom renderer to the sports combo box.

You also need to override the equals(...) and hashcode(...) methods of the Sport class. Java Collection methods rely on these methods to determine equality of a object instance when doing searches.

@Override
public boolean equals(Object object)
{
    Sport sport = (Sport)object;
    return this.name.equals( sport.getName() );
}

@Override
public int hashCode()
{
    return name.hashCode();
}

Also, read the Object API for more information on these methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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