JComboBox自定义单元格渲染器-如何获得组合的isEnabled()状态?



我已经为JComboBoxes创建了一个基于DefaultListCellRenderer的自定义单元格渲染器(我的一个基本显示颜色)。到目前为止,它运行得很好。

唯一的问题是,当JComboBox被禁用时:在这种情况下,我还应该用一种不那么闪亮的方式来绘制颜色(以显示它是非活动的)。但是我如何在ListCellRenderer中获得状态?

我尝试了isEnabled()或component.isEnabled(),但这似乎无法访问/给我组合的实际状态。在DefaultListCellRenderer中,有一个对list.isEnabled()的查询,但这对我来说没有意义(而且它也不起作用)。

任何想法?

我会采取一种实用的方式,将组合框传递给渲染器,例如

class ColorIcon implements Icon {
final Color color;
public ColorIcon(Color color) { this.color = color; }
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(color);
g.fillRect(x, y, getIconWidth(), getIconHeight());
}
@Override public int getIconWidth() { return 40; }
@Override public int getIconHeight() { return 12; }
}
Color[] colors = { Color.YELLOW, Color.RED, Color.LIGHT_GRAY, Color.BLUE, Color.GREEN };
JComboBox<Color> cmb = new JComboBox<>(colors);
cmb.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(
JList<?> list, Object value, int index, boolean sel, boolean focus) {
Color color = (Color)value;
Component c = super.getListCellRendererComponent(list, value, index, sel, focus);
setIcon(new ColorIcon(cmb.isEnabled()? color: color.darker()));
setText(String.format("#%06x", color.getRGB() & 0xFFFFFF));
return c;
}
});
JCheckBox chk = new JCheckBox("Disabled?");
chk.addChangeListener(ev -> cmb.setEnabled(!chk.isSelected()));
JFrame f = new JFrame("Test");
f.add(cmb, BorderLayout.PAGE_START);
f.add(chk, BorderLayout.PAGE_END);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);

这意味着您不能在不同的组合框之间共享此呈现器的单个实例,但这很少是一个问题。事实上,由于共享这样一个渲染器意味着在渲染过程中可能会使组件适应不同的父组件,因此共享可能会比实例化几个渲染器产生更高的成本。

你仍然可以使逻辑可共享,例如

private static JComboBox<Color> setupColorRender(JComboBox<Color> cmb) {
ListCellRenderer<? super Color> def = cmb.getRenderer();
ListCellRenderer<? super Color> r
= def instanceof JLabel? def: new DefaultListCellRenderer();
JLabel label = (JLabel)def;
cmb.setRenderer((list, color, index, sel, focus) -> {
Component c = r.getListCellRendererComponent(list, color, index, sel, focus);
label.setIcon(new ColorIcon(cmb.isEnabled()? color: color.darker()));
label.setText(String.format("#%06x", color.getRGB() & 0xFFFFFF));
return c;
});
return cmb;
}
Color[] colors = { Color.YELLOW, Color.RED, Color.LIGHT_GRAY, Color.BLUE, Color.GREEN };
JComboBox<Color> cmb = setupColorRender(new JComboBox<>(colors));
JCheckBox chk = new JCheckBox("Disabled?");
chk.addChangeListener(ev -> cmb.setEnabled(!chk.isSelected()));
JFrame f = new JFrame("Test");
f.add(cmb, BorderLayout.PAGE_START);
f.add(chk, BorderLayout.PAGE_END);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);

这个setupColorRender方法可以为任意数量的组合框调用。它进一步使用委托代替典型的子类,这允许使用原始的外观和感觉提供的渲染器,只要它仍然是JLabel的子类。这种方法对于某些外观有更好的视觉效果。感觉。

有点棘手,但可能;)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
/**
* <code>ComboTest</code>.
*/
public class ComboTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ComboTest()::startUp);
}
private void startUp() {
JFrame frm = new JFrame("Combo test");
JComboBox<String> combo = new JComboBox<>(new String[] {"One", "Two", "Three"});
combo.setRenderer(new EnablementCellRenderer());
JButton b = new JButton("Toggle Enabled");
b.addActionListener(l -> {
combo.setEnabled(!combo.isEnabled());
combo.repaint();
});
frm.add(combo);
frm.add(b, BorderLayout.EAST);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private class EnablementCellRenderer extends BasicComboBoxRenderer {
@Override
protected void paintComponent(Graphics g) {
JComboBox<?> combo = getFirstAncestorOfClass(this, JComboBox.class);
setForeground(combo.isEnabled() ? Color.BLUE : Color.GREEN);
super.paintComponent(g);
}
}
/**
* Searches for the first ancestor of the given component which is the instance of the given class.
*
* @param aStart start component to search. If the component is instance of the class - it will be returned.
* @param condition condition used to determine the component.
* @return first ancestor of the given component which is the instance of the given class. Null if no such component found.
*/
@Nullable
public static Container getFirstAncestor(Component aStart, Predicate<Component> condition) {
Container result = null;
Component base = aStart;
while ((result == null) && (base.getParent() != null || getInvoker(base) != null)) {
base = getInvoker(base) == null ? base.getParent() : getInvoker(base);
result = condition.test(base) ? (Container) base : null;
}
return result;
}
/**
* Searches for the first ancestor of the given component which is the instance of the given class.
*
* @param aStart start component to search. If the component is instance of the class - it will be returned.
* @param aClass class of component.
* @return first ancestor of the given component which is the instance of the given class. Null if no such component found.
* @param <E> class of component.
*/
@Nullable
public static <E> E getFirstAncestorOfClass(Component aStart, Class<E> aClass) {
return aClass.cast(getFirstAncestor(aStart, aClass::isInstance));
}
/**
* Gets the invoker of the given component when it's a pop-up menu.
*
* @param c component which invoker must be found.
* @return the invoker when the given component is a pop-up menu or null otherwise.
*/
private static Component getInvoker(Component c) {
return c instanceof JPopupMenu ? ((JPopupMenu) c).getInvoker() : null;
}
}

最新更新