【问题标题】:How can I replicate disabled appearance for a JCheckBox?如何复制 JCheckBox 的禁用外观?
【发布时间】:2010-06-29 07:31:49
【问题描述】:

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

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

我尝试为在绘画前后调用 setForeground 的复选框制作自定义 UI,但事实证明,通过调用 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);
            }
        });
    }
}

【问题讨论】:

  • 您想要 HTML 的任何具体原因?从上面的sn-p,我找不到任何东西。
  • 这只是复制问题的一个最小示例。真正的应用程序有一个粗体字来强调它,因为有两个看起来非常相似的复选框并排。

标签: java swing user-interface checkbox


【解决方案1】:

您可以将复选框和标签分离到它们自己的组件中,然后简单地制作一个没有标签的复选框。您也可以将它们添加到自己的面板中并覆盖面板的setEnabled() 方法以简单地启用/禁用复选框并更改标签的颜色。以这段代码sn-p为例:

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 和 label 必须是 final 才能在我们面板的 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 的设置器,让您可以随意更改它们。

【讨论】:

  • 修改渲染器也非常有用,正如 eugener 的建议。不过,我遇到了修改渲染器使其显示我的 HTML 标签的问题。有时它有点棘手,但它看起来确实干净了很多。 :)
  • 这个解决方案听起来需要修改应用程序中的每个视图。
【解决方案2】:

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

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

【讨论】:

  • 遗憾的是,JCheckBox 不支持单元格渲染器。否则我肯定会使用它,制作一个替换默认渲染器的自定义 UI。
  • 什么意思?我给了你如何完成的链接。只需创建你的自定义 ComboBoxRenderer :) 渲染器应该基于 ListCellRenderer。它确实有效 - 我用过很多次。
  • 你认为我会如何将组合框渲染器应用于复选框?
  • 对不起 :) 出于某种愚蠢的原因,我以为我们在谈论 JComboBox。就 JCheckBox 而言,这是一个众所周知的长期存在的错误 :(
【解决方案3】:

我不能再回答我自己的问题了……一定是与时区有关,并有时间在睡觉时考虑它。

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

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

然后这是实现,但这仍然很hacky,它使用一种实用方法来生成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);
    }
}

【讨论】:

    猜你喜欢
    • 2012-07-25
    • 2012-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    相关资源
    最近更新 更多