Java 如何为JCheckBox复制禁用的外观?

Java 如何为JCheckBox复制禁用的外观?,java,swing,user-interface,checkbox,Java,Swing,User Interface,Checkbox,以下面的示例代码为例。Swing中存在一些bug,如果组件包含HTML,它不会将禁用的组件呈现为禁用。除了报告这个问题(我希望一位同事已经注意到这个问题)之外,还有什么好办法来解决这个问题吗 无论我采取什么解决方案,我希望它是一个全局修复,而不是需要侵入应用程序中每个复选框的东西 我尝试为复选框创建一个自定义UI,在绘制之前和之后调用setForeground,但结果是,通过调用setForeground,它触发一个事件,最终导致调用repaint(),调用渲染器 import java.awt

以下面的示例代码为例。Swing中存在一些bug,如果组件包含HTML,它不会将禁用的组件呈现为禁用。除了报告这个问题(我希望一位同事已经注意到这个问题)之外,还有什么好办法来解决这个问题吗

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

我尝试为复选框创建一个自定义UI,在绘制之前和之后调用
setForeground
,但结果是,通过调用
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("<html><p>Enabled, HTML");
        JCheckBox checkBox3 = new JCheckBox("Disabled, plain text");
        checkBox3.setEnabled(false);
        JCheckBox checkBox4 = new JCheckBox("<html><p>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);
            }
        });
    }
}
导入java.awt.GridLayout;
导入java.util.array;
导入javax.swing.BorderFactory;
导入javax.swing.JCheckBox;
导入javax.swing.JComponent;
导入javax.swing.JFrame;
导入javax.swing.SwingUtilities;
公共类测试复选框扩展了JFrame
{
公共测试复选框()
{
JCheckBox checkBox1=新的JCheckBox(“已启用,纯文本”);
JCheckBox checkBox2=新的JCheckBox(“已启用,HTML”);
JCheckBox checkBox3=新的JCheckBox(“已禁用,纯文本”);
复选框3.setEnabled(假);
JCheckBox checkBox4=新的JCheckBox(“已禁用,HTML”);
复选框4.setEnabled(假);
setLayout(新的GridLayout(4,1));
for(JCheckBox复选框:Arrays.asList(复选框1、复选框2、复选框3、复选框4))
{
checkBox.setBorder(BorderFactory.createEmptyByOrder(8,8,8,8));
添加(复选框);
}
((JComponent)getContentPane()).setBorderFactory(BorderFactory.createEmptyBorder(8,8,8,8));
包装();
}
公共静态void main(字符串[]args)
{
SwingUtilities.invokeLater(新的Runnable()
{
@凌驾
公开募捐
{
TestCheckBox框架=新的TestCheckBox();
frame.setDefaultCloseOperation(关闭时退出);
frame.setVisible(true);
}
});
}
}

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


更多信息见

您可以将复选框和标签分离为各自的组件,只需制作一个没有标签的复选框即可。您也可以将它们添加到自己的面板中,并覆盖面板的
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);
请注意,
复选框和标签必须是最终的,才能在面板的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()
方法更改标签文本可能也很方便。不管你想要它做什么。甚至可能是
启用颜色
禁用颜色
的设置程序,允许您随意更改这些设置。

我必须停止回答我自己的问题。。。一定和时区有关,在睡觉的时候有时间思考

应用程序中的其他地方

UIManager.put("CheckBoxUI", "package.for.CustomisedWindowsCheckBoxUI");
然后这是实现,但这仍然是相当粗糙的,它使用一个实用的方法来生成HTML颜色字符串,这对于在这里发布来说不是很好

请注意,这只修复了Windows L&F的问题。Metal 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("<html><div style='color: %s'>%s",
                                                                  ColourUtils.toHtmlColour(b.getBackground().darker()),
                                                                  b.getText()));
                    }
                }
            }
        }

        super.paint(g, c);
    }
}
导入java.awt.Graphics;
导入javax.swing.AbstractButton;
导入javax.swing.JComponent;
导入javax.swing.plaf.ColorUIResource;
导入javax.swing.plaf.ComponentUI;
导入javax.swing.plaf.basic.BasicHTML;
导入javax.swing.text.View;
导入com.sun.java.swing.plaf.windows.WindowsCheckBoxUI;
导入com.blah.util.colorutils;
/**
*自定义Windows复选框UI以修复错误。
*/
公共类CustomisedWindowsCheckBoxUI扩展了WindowsCheckBoxUI{
/**
*从Swing调用的工厂方法。
*
*@param b选中复选框。
*@返回用户界面。
*/
公共静态组件UI createUI(JComponentB){
//TODO:Sun有一个AppContext,用于每个应用存储一次这些内容。
//使用这样的东西可能会更合群。
返回新的CustomisedWindowsCheckBoxUI();
}
@凌驾
公共空隙涂料(图形g、J组件c){
抽象按钮b=(抽象按钮)c;
//解决BasicButtonUI中的一个错误,其中文本中带有HTML标记的禁用按钮将被删除
//似乎没有被禁用。
//TODO:找到一种全局修复HTML呈现的方法。它不起作用似乎很奇怪。
//我可以在BasicHTML.createHTMLView中看到使用前景色的代码,它是
//显然,为什么setForeground()可以作为一种解决方法。
if(b.get前台()实例为ColorUIResource){
View=(View)c.getClientProperty(BasicHTML.propertyKey);
如果(视图!=null){
//确保在值未更改的情况下不更新渲染器。
String cachedHtmlFor=(String)c.getClientProperty(“cachedHtmlFor”);
String key=String.format(“%s:%s”,c.isEnabled(),b.getText());
如果(!key.equals(cachedHtmlFor)){
c、 putClientProperty(“cachedHtmlFor”,键);
如果(c.isEnabled()){
BasicHTML.updateRenderer(c,b.getText());
}否则{
巴斯