Java 在Swing中修改组合框显示

Java 在Swing中修改组合框显示,java,swing,jcombobox,Java,Swing,Jcombobox,我想修改(不可编辑)JComboBox的显示方式,使当前选择的条目在编辑字段(而不是下拉列表)中有一些额外的文本 大概是这样的: 我的第一个猜测是覆盖ComboBox的模型,以便getSelectedItem返回修改显示的包装器对象: petList.setModel(new ComboBoxModel() { private Object selected; public void setSelectedItem(Object anItem) { selec

我想修改(不可编辑)
JComboBox
的显示方式,使当前选择的条目在编辑字段(而不是下拉列表)中有一些额外的文本

大概是这样的:

我的第一个猜测是覆盖ComboBox的模型,以便
getSelectedItem
返回修改显示的包装器对象:

petList.setModel(new ComboBoxModel() {
    private Object selected;

    public void setSelectedItem(Object anItem) {
        selected = anItem;
    }

    public Object getSelectedItem() {
        return new ActiveComboItem(selected);
    }

    // … The rest of the methods are straightforward.
});
其中,
ActiveComboItem
如下所示:

static class ActiveComboItem {
    private final Object item;

    public ActiveComboItem(Object item) { this.item = item; }

    @Override
    public boolean equals(Object other) {
        return item == null ? other == null : item.equals(other);
    }

    @Override
    public String toString() { return String.format("Animal: %s", item); }
}
事实上,只要修改显示,这一点就行了。不幸的是,当前条目不再标记为活动:

(请注意缺少复选标记…或操作系统显示的选项。)

进一步检查表明,每次用户在框中选择新项时,都会调用模型的
getElementAt
方法,索引为
-1
。仅当使用修改后的选定项时,才会出现这种情况。当模型的
getSelectedItem
方法返回不带包装器的普通对象时,所选项在下拉框中标记为选中,并且
getElementAt
不使用参数
-1
调用

显然,组合框依次比较每个项目与当前活动的项目,但是,尽管我重写了
equals
方法,但它没有找到匹配项如何解决此问题?


(此问题的完整可编译代码)

您需要提供自定义ListCellRenderer。以下工作:

    final JComboBox animalCombo = new JComboBox(animals);
    animalCombo.setRenderer(new DefaultListCellRenderer() {
        @Override
        public Component getListCellRendererComponent(final JList list, Object value, final int index, final boolean isSelected,
                final boolean cellHasFocus) {

            if (index == -1) {
                value = "Animal: " + value;
            }

            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        }
    });
当其绘制的值不在下拉列表中时,索引为-1


作为将来的参考,当您只想更改Swing中的显示方式时,您永远不想修改支持模型。每个组件都有一个渲染器,通常您只需要稍微修改默认的渲染器。

使用
图标创建
JTextField
JButton
,因为
JButton
通过覆盖其方法
isRollover()、isPressed()、isArmed()

不要从
JComboBox
中提取
Icon
,绘制自己的三角形,然后
JButton
带有
Icon
将是
外观
本机操作系统
抵抗,以便更好地实现
JButton#setRolloverIcon()

创建一个
JPopup
JWindow
,放在这里
JScrollPane
,从
model.isPressed()或
isArmed显示此容器

现在你有两个选择

1) 创建包含
JCheckBox
JList
(从JCheckBox中删除矩形)

2) 或者在鼠标单击上实现并添加/删除
图标


隐藏
JPopup
JWindow
MouseListener
事件+1-不要修改模型以更改UI。相反,扩展渲染器以添加额外的片段。不幸的是,这不太管用。JComboBox在默认情况下似乎不使用
DefaultListCellRenderer
:使用上述代码,当前选定项目的显示与正常渲染不同(至少在OS X上)/编辑:它是
com.apple.laf.aquaComboxRender
。不过,这只需进行微小的修改即可工作。除了我的previos注释外:不要使用
super.GetListCellRenderComponent
,而是在设置新的渲染器之前存储以前使用过的渲染器,并使用其实现。@Konrad:是的,在不同的外观中支持默认渲染器实际上是一个更好的主意。谢谢。非常感谢。谢谢康拉德·鲁道夫。我扩展了一个JComboBox以使用自定义渲染器,并花了数小时试图使combobox的背景看起来正确。没有提前保存渲染,我只是调用super.getListCellRenderComponent,在使用windows LaF时以白色背景结束。当你将LaF设置为windows时,即使是oracle的自定义combo演示也有一个白色背景,因为它不尊重UI LaF.ehhh。。。这个随机码和OPs问题有关,因为?仅供记录:ComboBoxModel的实现是无效的,因为它不会在上触发ListModelEventsetSelectedItem@kleopatra很高兴知道。FWIW,该代码只是一个最小的尝试,在使其正确之前,首先使其工作。不点火是行为不当的常见原因:-)
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;

public class CheckList {

    public static void main(String args[]) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Create a list containing CheckListItem's
        JList list = new JList(new CheckListItem[]{
                    new CheckListItem("apple"),
                    new CheckListItem("orange"),
                    new CheckListItem("mango"),
                    new CheckListItem("paw paw"),
                    new CheckListItem("banana")});
        // Use a CheckListRenderer (see below) to renderer list cells
        list.setCellRenderer(new CheckListRenderer());
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        list.addMouseListener(new MouseAdapter() {// Add a mouse listener to handle changing selection

            @Override
            public void mouseClicked(MouseEvent event) {
                JList list = (JList) event.getSource();
                int index = list.locationToIndex(event.getPoint());// Get index of item clicked
                CheckListItem item = (CheckListItem) list.getModel().getElementAt(index);
                item.setSelected(!item.isSelected()); // Toggle selected state
                list.repaint(list.getCellBounds(index, index));// Repaint cell
            }
        });
        frame.getContentPane().add(new JScrollPane(list));
        frame.pack();
        frame.setVisible(true);
    }
}

// Represents items in the list that can be selected
class CheckListItem {

    private String label;
    private boolean isSelected = false;

    public CheckListItem(String label) {
        this.label = label;
    }

    public boolean isSelected() {
        return isSelected;
    }

    public void setSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }

    @Override
    public String toString() {
        return label;
    }
}

// Handles rendering cells in the list using a check box
class CheckListRenderer extends JCheckBox implements ListCellRenderer {

    private static final long serialVersionUID = 1L;

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
        setEnabled(list.isEnabled());
        setSelected(((CheckListItem) value).isSelected());
        setFont(list.getFont());
        setBackground(list.getBackground());
        setForeground(list.getForeground());
        setText(value.toString());
        return this;
    }
}