简体   繁体   English

JComboBox 单元格渲染器在 Windows 外观和感觉中失败

[英]JComboBox Cell Renderer Fails with Windows Look and Feel

I am writing a Java application that uses the local system look and feel.我正在编写一个使用本地系统外观的 Java 应用程序。 In the program there is a ListCellRenderer that renders a colored dot (a custom JComponment) followed by some text given an object.在程序中有一个 ListCellRenderer,它呈现一个彩色点(一个自定义 JComponment),然后是给定对象的一些文本。 This works fine when using Swing's default Metal look and feel.这在使用 Swing 的默认 Metal 外观和感觉时效果很好。

However, when I use the Windows Look and feel, the cells are rendered correctly in the drop-down list, but the selected item (the one displayed when the user is not in the act of selecting a different option) renders only the text and ignores the colored dot.但是,当我使用 Windows 外观时,单元格在下拉列表中正确呈现,但所选项目(当用户没有选择其他选项时显示的项目)仅呈现文本和忽略彩色点。 If I change the renderer to set the font, the proper font is observed in both the drop down and the selected item, so I know that the cell renderer is being used, at least in part.如果我更改渲染器以设置字体,则会在下拉列表和所选项目中观察到正确的字体,因此我知道至少部分使用了单元格渲染器。

I've read some posts around the web about the different LAFs causing problems like this but haven't come across anyone who's discussing my particular problem.我在网上阅读了一些关于导致此类问题的不同 LAF 的帖子,但没有遇到任何人在讨论我的特定问题。

In case anyone is curious here is the code:如果有人好奇这里是代码:

. .

@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;
}

Also, here's the code for the custom component inc ase anyone wants to try this out to see if I'm just doing something silly here:此外,这里是自定义组件的代码,因为有人想尝试一下,看看我是否只是在这里做一些愚蠢的事情:

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;
    }
}

Edit: I just tested this on Ubuntu 12.04 and the cell renderer worked as expected there (although the JCombobox didn't render its outside border as it does if no custom renderer is applied).编辑:我刚刚在 Ubuntu 12.04 上测试了这个,并且单元渲染器在那里按预期工作(尽管 JCombobox 没有像没有应用自定义渲染器那样渲染它的外边框)。

Edit: As I look into this more and more, it seems as though there may be something to the setEditor method on JComboBox, however when not editable, the renderer should be used as the javadoc for the method states:编辑:随着我越来越多地研究这个,似乎 JComboBox 上的 setEditor 方法可能有一些东西,但是当不可编辑时,渲染器应该用作该方法状态的 javadoc:

Sets the editor used to paint and edit the selected item in the JComboBox field.设置用于在 JComboBox 字段中绘制和编辑所选项目的编辑器。 The editor is used only if the receiving JComboBox is editable.仅当接收 JComboBox 可编辑时才使用编辑器。 If not editable, the combo box uses the renderer to paint the selected item.如果不可编辑,组合框将使用渲染器绘制所选项目。

That just doesn't appear to be the behavior that I'm seeing.这似乎不是我所看到的行为。 What must I do in order to have ALL parts of my cell renderer be observed for users of the Windows LAF?我必须做什么才能让 Windows LAF 的用户观察到我的单元格渲染器的所有部分?

Digging into this, I found that for the Windows LAF, I need to set a ComboBoxEditor and set the JComboBox to be editable in order for the selected cell to render properly.深入研究,我发现对于 Windows LAF,我需要设置一个 ComboBoxEditor 并将 JComboBox 设置为可编辑,以便所选单元格正确呈现。

This appears to me to be a bug/unintended function specific to the Windows Look and Feel as the API for JComboBox's setEditor method states that when not editable, the renderer will be used - and it is when run in both default Metal LAF and on Ubuntu.在我看来,这是一个特定于 Windows 外观和感觉的错误/意外功能,因为 JComboBox 的 setEditor 方法的 API 指出,当不可编辑时,将使用渲染器 - 它是在默认 Metal LAF 和 Ubuntu 上运行时.

In addition to that, I wasn't able to just have the Editor return a new cell each time getEditorComponent was called as one does in a ListCellRenderer.除此之外,我无法像在 ListCellRenderer 中那样每次调用 getEditorComponent 时让编辑器返回一个新单元格。 Which I suppose makes sense.我认为这是有道理的。

This website provides an example (albeit sort of a lackluster one) of how to create an editor:这个网站提供了一个如何创建编辑器的例子(虽然有点乏味):

The API for the JComboBox and BasicComboBox were also helpful: JComboBox 和 BasicComboBox 的 API 也很有帮助:

And finally, my Editor Code:最后,我的编辑器代码:

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.");
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM