JComboBox单元格渲染器因Windows外观失败

时间:2015-06-14 02:46:28

标签: java windows swing jcombobox listcellrenderer

我正在编写一个使用本地系统外观的Java应用程序。在程序中有一个ListCellRenderer,它呈现一个彩色点(一个自定义JComponment),然后是一个给定对象的文本。这在使用Swing的默认Metal外观时效果很好。

但是,当我使用Windows外观时,单元格在下拉列表中正确呈现,但所选项目(当用户不选择其他选项时显示的项目)仅呈现文本并忽略彩色圆点。如果我更改渲染器以设置字体,则在下拉列表和选定项目中都会观察到正确的字体,因此我知道正在使用单元格渲染器,至少部分是这样。

我已经在网上阅读了一些关于不同LAF的帖子,这些帖子会导致这样的问题,但是没有人会遇到任何讨论我特定问题的人。

如果有人好奇,这里是代码:

@Override
public Component getListCellRendererComponent(JList<? extends ColoredDisplayable> jlist, ColoredDisplayable e, int i, boolean isSelected, boolean hasFocus) {

    JPanel cell = new JPanel(new GridBagLayout());
    cell.setOpaque(false);

    JLabel label = new JLabel(e.getDisplayString());
    label.setOpaque(false);
    label.setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4));
    label.setHorizontalAlignment(JLabel.LEFT);

    Color deselectedBackground = cell.getBackground();
    Color deselectedTextColor = cell.getForeground();

    // LAYOUT COMPONENTS
    // Dot
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.insets = INSETS;
    gbc.anchor = GridBagConstraints.LINE_START;
    gbc.weightx = 0.0f;
    gbc.fill = GridBagConstraints.NONE;
    cell.add(new Dot(e.getColor()), gbc);

    // Label
    gbc.gridx = 1;
    gbc.weightx = 1.0f;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    cell.add(label, gbc);


    if (isSelected){
        cell.setOpaque(true);
        cell.setBackground(MetalLookAndFeel.getTextHighlightColor());
    } else {
        cell.setBackground(deselectedBackground);
        cell.setForeground(deselectedTextColor);
    }

    return cell;
}

此外,这里是自定义组件的代码,任何人都想尝试这一点,看看我是否只是在做一些愚蠢的事情:

public class Dot extends JComponent {

    /** The size of the dot. */
    private static final int SIZE = 10;

    /** The size of the dot. */
    private static final int PAD = 4;

    private static final Dimension DIM = new Dimension(SIZE + PAD, SIZE + PAD);

    /** The Color to render the dot. */
    private final Color m_color;

    /** The Dot itself. */
    private static final Ellipse2D.Double DOT = new Ellipse2D.Double(PAD / 2, PAD / 2, SIZE, SIZE);

    /**
     * Creates a dot of the specified color.
     * @param color the color to make the dot.
     */
    public Dot(Color color) {
        m_color = color;
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(m_color);
        g2d.fill(DOT);
    }

    @Override
    public Dimension getPreferredSize() {
        return DIM;
    }
}

编辑:我刚刚在Ubuntu 12.04上对此进行了测试,并且单元格渲染器在那里按预期工作(尽管JCombobox没有像未应用自定义渲染器那样渲染其外部边框)

编辑:当我越来越多地研究这个问题时,似乎JComboBox上的setEditor方法可能有些东西,但是当不可编辑时,渲染器应该用作javadoc for方法陈述:

  

设置用于在JComboBox字段中绘制和编辑所选项目的编辑器。仅当接收JComboBox可编辑时才使用编辑器。如果不可编辑,则组合框使用渲染器绘制选定的项目。

这似乎并不是我所看到的行为。我必须做些什么才能让Windows LAF的用户看到我的单元格渲染器的所有部分?

1 个答案:

答案 0 :(得分:2)

深入研究,我发现对于Windows LAF,我需要设置一个ComboBoxEditor并将JComboBox设置为可编辑,以便正确呈现所选单元格。

在我看来,这是一个特定于Windows外观的错误/意外功能,因为JComboBox的setEditor方法的API指出,当不可编辑时,将使用渲染器 - 它在运行时默认的金属LAF和Ubuntu。

除此之外,每次调用getEditorComponent时,我都无法让Editor返回一个新单元格,就像在ListCellRenderer中一样。我认为这是有道理的。

此网站提供了如何创建编辑器的示例(尽管有点黯淡):

JComboBox和BasicComboBox的API也很有帮助:

最后,我的编辑代码:

public class ColoredDisplayableComboBoxEditor extends BasicComboBoxEditor {

    private ColoredDisplayable m_cd = null;
    private static final Insets INSETS = new Insets(3, 1, 3, 1);
    private final JPanel m_cell;
    private final JLabel m_label;
    private final Dot m_dot;

    public ColoredDisplayableComboBoxEditor() {
        // INITIALIZE
        // Panel
        m_cell = new JPanel(new GridBagLayout());
        m_cell.setOpaque(false);

        // Label
        m_label = new JLabel();
        m_label.setOpaque(false);
        m_label.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
        m_label.setHorizontalAlignment(JLabel.LEFT);

        // Dot
        m_dot = new Dot(Color.BLACK);

        // LAYOUT
        // Dot
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = INSETS;
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.weightx = 0.0f;
        gbc.fill = GridBagConstraints.NONE;
        m_cell.add(m_dot, gbc);

        // Label
        gbc.gridx = 1;
        gbc.weightx = 1.0f;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        m_cell.add(m_label, gbc);
    }

    @Override
    public Component getEditorComponent() {
        return m_cell;
    }

    @Override
    public Object getItem() {
        System.out.println("getting item.");
        return m_cd;
    }

    @Override
    public void setItem(Object item) {
        System.out.println("setting item.");
        if (item instanceof ColoredDisplayable) {
            ColoredDisplayable cd = (ColoredDisplayable)item;
            if (!cd.equals(m_cd)) {
                System.out.println("--item actually set.");
                m_cd = cd;
                m_label.setText(m_cd.getDisplayString());
                m_dot.setColor(m_cd.getColor());
            }            
        } else {
            throw new IllegalArgumentException("Parameter item must be a ColoredDisplayable.");
        }
    }
}