如何复制JCheckBox的禁用外观?

请参考以下示例代码。 Swing中存在一些错误,如果组件包含HTML,则不会将禁用的组件呈现为禁用。 除了报告我希望同事已经处理过的问题之外,还有一些解决问题的好方法吗?

无论我采取什么样的解决方案,我希望它是一个全局修复,而不是需要被攻击到应用程序中的每个复选框的东西。

我尝试为绘制前后调用setForeground的复选框创建自定义UI,但事实certificate,通过调用setForeground ,它会触发一个事件,最终导致调用repaint() ,调用渲染器,…

 import java.awt.GridLayout; import java.util.Arrays; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class TestCheckBoxes extends JFrame { public TestCheckBoxes() { JCheckBox checkBox1 = new JCheckBox("Enabled, plain text"); JCheckBox checkBox2 = new JCheckBox("

Enabled, HTML"); JCheckBox checkBox3 = new JCheckBox("Disabled, plain text"); checkBox3.setEnabled(false); JCheckBox checkBox4 = new JCheckBox("

Disabled, HTML"); checkBox4.setEnabled(false); setLayout(new GridLayout(4, 1)); for (JCheckBox checkBox : Arrays.asList(checkBox1, checkBox2, checkBox3, checkBox4)) { checkBox.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); add(checkBox); } ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); pack(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TestCheckBoxes frame = new TestCheckBoxes(); frame.setDefaultCloseOperation(EXIT_ON_CLOSE); frame.setVisible(true); } }); } }

您可以将复选框和标签分隔为各自的组件,只需创建一个没有标签的复选框。 您也可以将它们添加到自己的面板中,并覆盖面板的setEnabled()方法,只需启用/禁用复选框并更改标签的颜色即可。 以此代码段为例:

 final JCheckBox checkbox = new JCheckBox(); final JLabel label = new JLabel(); JPanel panel = new JPanel() { @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); checkbox.setEnabled(enabled); if (enabled) label.setForeground(Color.BLACK); else label.setForeground(Color.GRAY); } }; panel.add(checkbox); panel.add(label); 

请注意, checkbox和标签必须是最终的才能在我们的面板的setEnabled()方法中使用它们。 根据您将HTML插入复选框的频率,您始终可以创建自己的组件类来执行此操作。

 public class HTMLCheckBox extends JPanel { private JCheckBox checkbox = new JCheckBox(); private JLabel label = new JLabel(); private Color disabledColor = Color.GRAY; private Color enabledColor = Color.BLACK; public HTMLCheckBox(String text) { label.setText(text); add(checkbox); add(label); } public boolean isSelected() { return checkbox.isSelected(); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); checkbox.setEnabled(enabled); if (enabled) label.setForeground(enabledColor); else label.setForeground(disabledColor); } } 

然后根据需要添加自己的构造函数和方法。 例如,覆盖setBackground()以使其设置面板,复选框和标签的背景。 用于更改标签文本的setText()方法可能也很方便。 无论你想要它做什么。 甚至可能是enabledColordisabledColor setter允许你enabledColor更改这些。

我建议开始使用渲染器而不是那个。 您可以在渲染器中使用HTML(例如JLabel),您的问题将自行消失:)

更多信息请访问http://java.sun.com/docs/books/tutorial/uiswing/components/combobox.html#renderer

我必须停止回答我自己的问题……必须与时区有关,并有时间在睡眠时考虑它。

应用程序中的其他位置……

 UIManager.put("CheckBoxUI", "package.for.CustomisedWindowsCheckBoxUI"); 

然后这是实现,但这仍然非常hacky并且它使用实用程序方法生成HTML颜色字符串,这对于在此处发布不太好。

请注意,这仅适用于Windows L&F。 金属L&F也见证了有问题,但解决方案是相同的,只是子类BasicCheckBoxUI而已。

 import java.awt.Graphics; import javax.swing.AbstractButton; import javax.swing.JComponent; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.View; import com.sun.java.swing.plaf.windows.WindowsCheckBoxUI; import com.blah.util.ColourUtils; /** * Customisation of Windows check box UI to fix bugs. */ public class CustomisedWindowsCheckBoxUI extends WindowsCheckBoxUI { /** * Factory method called from Swing. * * @param b the check box. * @return the UI. */ public static ComponentUI createUI(JComponent b) { // TODO: Sun have an AppContext they use to store these once per app. // Might be more sociable to use something like that. return new CustomisedWindowsCheckBoxUI(); } @Override public void paint(Graphics g, JComponent c) { AbstractButton b = (AbstractButton) c; // Works around a bug in BasicButtonUI where a disabled button with HTML markup in the text will // not appear to be disabled. // TODO: Find a way to fix this globally for HTML rendering. It seems odd that it isn't working. // I can see the code in BasicHTML.createHTMLView which uses the foreground colour, which is // obviously why setForeground() works as a workaround. if (b.getForeground() instanceof ColorUIResource) { View view = (View) c.getClientProperty(BasicHTML.propertyKey); if (view != null) { // Ensure that we don't update the renderer if the value hasn't changed. String cachedHtmlFor = (String) c.getClientProperty("cachedHtmlFor"); String key = String.format("%s:%s", c.isEnabled(), b.getText()); if (!key.equals(cachedHtmlFor)) { c.putClientProperty("cachedHtmlFor", key); if (c.isEnabled()) { BasicHTML.updateRenderer(c, b.getText()); } else { BasicHTML.updateRenderer(c, String.format("
%s", ColourUtils.toHtmlColour(b.getBackground().darker()), b.getText())); } } } } super.paint(g, c); } }