JTable 中可编辑 JComboBox 的占位符

Placeholder for editable JComboBox in JTable

我有一个 JTable,其中一列(第 1 列)是一个 JComboBox,它允许从列表中创建选项,并且可以向其中输入新选项。 MWE:

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.TableColumn;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;

public class ComboTableDemo extends JPanel {

    public ComboTableDemo() {
        super(new GridLayout(1,0));
        final String[] headings = {"Name", "Option"};
        final String string1 = "Foo";
        final String string2 = "Bar";
        Object[][] data = {
                {"Albert", string1},
                {"Bob", null},
                {"Clare", null},
                {"David", null}
        };

        final JTable table = new JTable(data, headings);
        table.setPreferredScrollableViewportSize(new Dimension(300, 100));
        table.setFillsViewportHeight(true);

        final String[] optionsInit = new String[] {string1, string2};
        HashSet<String> options = new HashSet<String>(Arrays.asList(optionsInit));
        JComboBox<String> optionsCombo = new JComboBox<String>(optionsInit);

        optionsCombo.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ev) {
                String newSelection = (String)optionsCombo.getSelectedItem();
                if(!options.contains(newSelection)) {
                    options.add(newSelection);
                    optionsCombo.addItem(newSelection);
                }
            }

        });
        optionsCombo.setEditable(true);
        TableColumn column = table.getColumnModel().getColumn(1);
        column.setCellEditor(new DefaultCellEditor(optionsCombo));

        add(new JScrollPane(table));
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("ComboTableDemo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                ComboTableDemo pane = new ComboTableDemo();
                pane.setOpaque(true);
                frame.setContentPane(pane);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

在 table 中,用户可以并且应该为空条目输入值并不明显,因此我想包含占位符文本以明确这一点。我已经看到 elsewhere that a custom ListCellRenderer can be provided with setRenderer in the case of uneditable combos, but in the case of editable combos (as explained in the tutorial) 似乎必须使用 setEditor 提供 ComboBoxEditor。是否有一个简单的实现,或者更好的方法来达到同样的目的?

实际上,JTable 中的输入组件是一种特殊情况,因为它们仅在编辑单元格时处于活动状态。因此,除了设置编辑器以控制值的编辑方式外,您还需要更改渲染器以控制所选值的呈现方式,

    // As before:
    TableColumn column = table.getColumnModel().getColumn(1);
    column.setCellEditor(new DefaultCellEditor(optionsCombo));
    // Additional line to set renderer:
    column.setCellRenderer(new PlaceholderRenderer("<choose or add option>"));

这里的 PlaceholderRenderer 应该是一个 TableCellRenderer 实现,当没有选择任何值时显示占位符字符串。例如:

import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

public class PlaceholderRenderer extends DefaultTableCellRenderer {

    final private String placeholder;

    public PlaceholderRenderer(String placeholder) {
        super();
        this.placeholder = placeholder;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table,
            Object value,
            boolean isSelected,
            boolean hasFocus,
            int row,
            int column) {
        if ((value == null) || (value.equals(""))) { 
            return super.getTableCellRendererComponent(table, this.placeholder, isSelected, hasFocus, row, column);  
        } else { 
            return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);  
        }
    }

}