让 JComboBox 在 JTable 中显示而不先单击它

Posted

技术标签:

【中文标题】让 JComboBox 在 JTable 中显示而不先单击它【英文标题】:Getting a JComboBox to Display in a JTable without Clicking it First 【发布时间】:2022-01-13 14:35:59 【问题描述】:

我正在尝试了解如何将 JComboBox 放入 JTable,因此我正在使用 JTable 教程中的演示。

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;

/** 
 * TableRenderDemo is just like TableDemo, except that it
 * explicitly initializes column sizes and it uses a combo box
 * as an editor for the Sport column.
 */
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",
     "Snowboarding", new Integer(5), new Boolean(false),
    "John", "Doe",
     "Rowing", new Integer(3), new Boolean(true),
    "Sue", "Black",
     "Knitting", new Integer(2), new Boolean(false),
    "Jane", "White",
     "Speed reading", new Integer(20), new Boolean(true),
    "Joe", "Brown",
     "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();
        
    );


通常,在单击之前,JComboBox 不会显示。我想让它一直显示,以便用户知道该列中有一个组合框。我已经尝试过自定义渲染器(一些在这个论坛上找到)来解决这个问题,但遇到的问题让我走上了一条非常错误的道路。是否有一个示例显示如何在不先单击的情况下显示组合框。 TIA。

【问题讨论】:

“显示”单元格内容是CellRenderer 的责任。我建议你看看Using Custom Renderers 这不是 MCVE。我已经告诉过你如何创建一个。您的问题是关于组合框的渲染器。所以你只需要一个单列的表。不需要所有与您的问题无关的调试逻辑或“printDebugData()”方法。不需要自定义表格模型。您可以只使用 DefaultTableModel 并将自定义渲染器添加到表中的特定列。我之前告诉过你,整个 MCVE 将是 20-30 行代码。 那我不知道你想要什么。这是我正在运行并试图以我想要的方式显示的程序。我还能发布什么? What else would I possibly post? - 我告诉过你所有的调试逻辑都是不必要的。你有什么不明白???我告诉过您不需要自定义 TableModel,您可以使用 DefaultTableModel。你有什么不明白???。 MCVE 的目的是将所述问题简化为尽可能少的代码行。这告诉我们,您实际上理解了您提出的问题,并且您已经努力解决问题。无论如何,即使有所有这些问题,我仍然给你一个答案。你对这个答案有什么不明白的地方? 【参考方案1】:

渲染器看起来像:

class ComboBoxRenderer extends JComboBox implements TableCellRenderer

    public ComboBoxRenderer()
    
        setBorder(null);
    

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

        return this;
    

【讨论】:

宾果游戏!这正是我想要的。谢谢。【参考方案2】:

您可以使用以下代码在第一次点击时调用组合框编辑器。

JTable table = new JTable(new MyTableModel()) 
    @Override
    public boolean editCellAt(int row, int column, EventObject e)
        boolean result = super.editCellAt(row, column, e);
        if( result ) 
            Component comp = getEditorComponent();
            if( comp instanceof JComboBox ) 
                final JComboBox combo = ( JComboBox ) comp;
                SwingUtilities.invokeLater( new Runnable() 

                    @Override
                    public void run() 
                        combo.showPopup();
                        // TODO Auto-generated method stub

                    
                );
            
        
        return result;
    
;  

【讨论】:

以上是关于让 JComboBox 在 JTable 中显示而不先单击它的主要内容,如果未能解决你的问题,请参考以下文章

浅析JTable与TableModelTableCellRendererTableCellEditor接口——使用JComboBox显示单元格的值

请教关于Jtable与Jcombobox结合使用的一个问题

在Jtable单元格中单击JComboBox时调用ActionPerformed

如何在 Swing 中的 JTable 的列中添加不同的 JComboBox 项

单击以打开 JComboBox 时,JTable 单元格失去价值

JTable和JTable的TableColumn中的BeansBinding