Below code to display a JTable
with 3 columns, which respectively contain a JComboBox
, a String
and a double
, and which should display yellow. The problem is that I cannot get the JComboBox
in the first column to display as … a combo box; instead I get a String
saying “javax.swing.JComboBox...
“. What am I doing wrong?
import javax.swing.*; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableModel; import java.awt.*; public class BadDialog extends JDialog { //Instantiate the data for the table, which is 2 rows x 3 cols private final JComboBox col0ComboBox = new JComboBox(new String[]{"aaa", "bbb"}); //Goes in all rows of Col 0 private final String[] col1Data = {"Mickey", "Mouse"}; private final double[] col2Data = {111, 222}; public BadDialog() { //Instantiate table JTable badTable = new JTable(); //Assign a tableModel to the table, put the table in a scroller, add it to this dialog, and sort out the renderer TableModel badTableModel = new BadTableModel(); badTable.setModel(badTableModel); JScrollPane scroller = new JScrollPane(badTable); add(scroller); BadTableCellRenderer badTableCellRenderer = new BadTableCellRenderer(); badTable.setDefaultRenderer(JComboBox.class, badTableCellRenderer); //Col 0 badTable.setDefaultRenderer(String.class, badTableCellRenderer); //Col 1 badTable.setDefaultRenderer(Double.class, badTableCellRenderer); //Col 2 //Assign col0ComboBox to Col 0 badTable.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(col0ComboBox)); //Show the dialog setPreferredSize(new Dimension(300, 470)); pack(); setModal(true); setLocation(10, 10); setVisible(true); } private final class BadTableModel extends AbstractTableModel { @Override public int getRowCount() { return 2; } @Override public int getColumnCount() { return 3; } @Override public Object getValueAt(int rowIndex, int colIndex) { if (colIndex == 0) return col0ComboBox; if (colIndex == 1) return col1Data[rowIndex]; return col2Data[rowIndex]; } @Override public Class<?> getColumnClass(int colIndex) { if (colIndex == 0) return JComboBox.class; if (colIndex == 1) return String.class; return Double.class; } } private static class BadTableCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); //Make all columns yellow c.setBackground(Color.YELLOW); c.setForeground(Color.RED); c.setFont(new Font("Dialog", Font.PLAIN, 12)); return c; } } public static void main(String[] args) { new BadDialog(); } }
Advertisement
Answer
Never return a component in a TableModel. The whole point of having a separate model and view is that the model contains only data, not components. The model’s job is to provide data; the view’s job is to determine how to display that data.
Your TableModel’s getColumnClass method should look like this:
public Class<?> getColumnClass(int colIndex) { if (colIndex == 0) return String.class; // String, not JComboBox if (colIndex == 1) return String.class; return Double.class; }
and your getValueAt method needs to return the actual data value at that row:
public Object getValueAt(int rowIndex, int colIndex) { if (colIndex == 0) return (rowIndex % 1 == 0 ? "aaa" : "bbb"); if (colIndex == 1) return col1Data[rowIndex]; return col2Data[rowIndex]; }
The cell renderer is part of the view, not the model, so it can make use of a JComboBox. Your render needs to use the value
argument to modify your JComboBox:
private static class BadTableCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { if (row != 0) { return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); } JComboBox c = col0ComboBox; c.setSelectedItem(value); //Make all columns yellow c.setBackground(Color.YELLOW); c.setForeground(Color.RED); c.setFont(new Font("Dialog", Font.PLAIN, 12)); return c; } }