我正在编写一个使用本地系统外观的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的用户看到我的单元格渲染器的所有部分?
答案 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.");
}
}
}