更改所选JToggleButton的背景颜色

11
我正在尝试以一种可靠、与外观无关的方式更改选中的JToggleButton的颜色。
如果使用Metal L&F,则可以使用UIManager来实现:
UIManager.put("ToggleButton.selected", Color.RED);

注意:Iyy指出我在上面的属性名称中有一个拼写错误,但我会保留它供到达此处的人参考,但实际的属性名称应该是:
UIManager.put("ToggleButton.select", Color.RED);

然而,在我的当前外观(目前是Windows XP)中,这并不起作用。经过进一步分析,似乎Windows中的系统外观(仍然是XP)根本不使用任何基于颜色的UIManager属性来设置ToggleButton,或者至少它自己没有提供它们(有一个快速的在线示例可以找到UIManager的所有属性键,该示例方便地明确限制为颜色属性)。
我尝试过设置背景颜色:
Action action = new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) { /* stuff */ }
};
JToggleButton button = new JToggleButton(action);
// tried with and without opaque true
button.setOpaque(true);
button.setBackground(Color.RED);

不仅它不改变所选状态,而且它甚至不影响未选状态。
我尝试在接收到动作后仅更改背景颜色。
@Override
public void actionPerformed(ActionEvent e)
{
    JToggleButton button = (JToggleButton)e.getSource();
    if (button.isSelected()) // alternatively, (Boolean)getValue(Action.SELECTED_KEY)
    {
        button.setBackground(Color.RED);
    }
}

没有一个方法有效。我唯一发现有效的方法是需要我自己在选中状态下绘制按钮(这会导致一个有效的示例,尽管外观不符合标准):
private class ColoredToggleButton extends JToggleButton
{
    ColoredToggleButton(Action action, Color color)
    {
        super(action);

        setBackground(color);
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (this.isSelected())
        {
            int w = getWidth();
            int h = getHeight();
            String s = getText();

            // selected color
            g.setColor(getBackground());
            g.fillRect(0, 0, w, h);
            // selected foreground color
            g.setColor(SystemColor.controlText);
            g.drawString(s,
                         (w - g.getFontMetrics().stringWidth(s)) / 2 + 1,
                         (h + g.getFontMetrics().getAscent()) / 2 - 1);
        }
    }
}

这是从这个Java bug report中稍作修改的评论。有趣的是(令人发笑的是?),它声称在1998年已经修复。
有人知道有没有更好的、与L&F无关的方法来设置选中的JToggleButton的背景颜色吗?

你可以覆盖BasicButtonUI或者只是MetalButtonUI(仅适用于Metal L&F)。一些提示请参考https://dev59.com/mm025IYBdhLWcg3w3J5H#5755124。 - mKorbel
为什么?LAF应该控制外观,最好让它尽职尽责-如果干扰成功,你的自定义绘画/颜色将在所有情况下显得突兀。 - kleopatra
1
@kleopatra,我通常同意离开L&F是一个好的实践,但是这是一个期望的特性。特别地,它实际上是为了突出显示而设计的。 - pickypg
6个回答

8
JToggleButton btn = new JToggleButton(...);
btn.setUI(new MetalToggleButtonUI() {
    @Override
    protected Color getSelectColor() {
        return Color.RED;
    }
});

6
"

ToggleButton.selected" 是错误的,应该是 "ToggleButton.select"。并且应该更新组件。

"
UIManager.put("ToggleButton.select", Color.WHITE);
SwingUtilities.updateComponentTreeUI(togglebuttonname);

这是一个很好的发现,确实有效!然而,有一个相当大的警告,它会全局影响所有的 JToggleButton,除非你创建自己的实例并为其提供一个新的 static ComponentUI createUI(JComponent c)。因此,我仍然被困在自己的代码中,但对于那些想要全局更改背景颜色的人来说,你的方法是可行的解决方案。 - pickypg
我太激动了,拼写错误是个好发现。然而,在Windows系统的外观和感觉中不起作用。 - pickypg
进一步研究后发现,L&F系统甚至不使用与“Color”相关的任何ToggleButton键(例如,“ToggleButton.background”)。 - pickypg

3
你可以尝试使用 setIcon() 方法来达到你的目的,但你也可以在 ButtonUI 代理中重写 paint() 方法。
补充说明:@kleopatra 的评论很有道理:改变 UI 代理并不是一件简单的事情。@mKorbel 最近的示例展示了这种方法的难度和多样性。它的主要优点是与外观无关。
一些较为简单的方法在这里提到。

回复:实现自定义ButtonUI/子类化现有的 - 当然,一切皆有可能 - 但并不是所有事情都可行;-) 我强烈反对。 - kleopatra
@kleopatra:感谢您的评论;我已经尝试从更高的角度来看待这个问题。 - trashgod
@ Jeanette,我一直很感激你给我的警醒,因为它总是关于我在胡说八道什么,但 OP 可能不想听。如果不可能的话,等待 BugsParade 的补丁,然后再回复你。如果你对 setForeground 或 setIcon 的选项不满意,可以创建自定义组件。 - mKorbel
我扩展了BasicToggleButtonUI并重新实现了绘制逻辑,除了正确的背景颜色之外。然而,缺乏真正的可移植性(它只会在我的XP主题中看起来正确)使其几乎毫无价值,特别是考虑到显着增加的复杂性(以隐藏错误为代价),更不用说用户必须调用toggleButton.setUI(myImpl)。我将选择我列出的最终选项(如果选择,则手动扩展和绘制),并希望JDK的未来版本实际支持背景属性。它不是很漂亮,但它能够完成工作。 - pickypg

3

当使用"Windows外观"时,在Netbeans中您只需要做两件事情。

  1. 进入属性
  2. 取消选择'contentAreaFilled'
  3. 选择'opaque'
  4. 您可以在属性或硬编码中设置背景颜色

另一种方法是,

    jToggleButton.setContentAreaFilled(false);
    jToggleButton.setOpaque(true);
    jToggleButton.setBackground(Color.red); //Your color here

这是全部内容。:-)

1
你可以在每次重绘之前简单地强制背景颜色-为此,你需要修改paintComponent,检查按钮是否被切换,根据切换状态设置背景,并最后让超类完成实际的绘画工作:
public class ColoredToggleButton extends JToggleButton
{
    @Override
    public void paintComponent(Graphics g)
    {
        Color bg;
        if (isSelected()){
            bg = Color.GREEN;
        } else {
            bg = Color.RED;
        }
        setBackground(bg);
        super.paintComponent(g);
    }
}

对我不起作用。如果按钮被选中,Swing不使用提供的背景颜色。这适用于普通的JButton,但您必须自己实现选择/取消选择逻辑(这非常容易)。 - user6315137

0

如果您宁愿使用动作监听器而不是覆盖 UI 中的方法,您可以将 UI 更改为没有任何 selectColor 属性的 UI。

这是我最近使用的一个示例。

private class FavouriteToggle extends JToggleButton {
    public FavouriteToggle() {
        setUI(new BasicToggleButtonUI()); //Removes selectColor

        ////Your Custom L&F Settings////
        setBackground(new Color(255, 252, 92));
        setForeground(Color.GRAY);
        setText("Favourite");
        setBorder(null);
        setFocusPainted(false);

        ////Add your own select color by setting background////
        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(((JToggleButton) e.getSource()).isSelected()) {
                    setForeground(Color.BLACK);
                    setBackground(new Color(255, 251, 0));
                } else {
                    setBackground(new Color(255, 252, 92));
                    setForeground(Color.GRAY);
                }
            }
        });
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接