Changing the background color of a selected JToggleButton
Asked Answered
B

6

11

I am trying to change the color of a JToggleButton when it has been selected in a reliable, look and feel independent way.

If using the Metal L&F, then using the UIManager is an approach:

UIManager.put("ToggleButton.selected", Color.RED);

Note: Iyy pointed out that I had a typo in the property name above, but I will leave it above for people getting here, but the actual property name is supposed to be:

UIManager.put("ToggleButton.select", Color.RED);

However, this does not work in my current Look and Feel (currently Windows XP). After some further analysis, it appears that the system look and feel in Windows (still XP) does not use any of the Color-based UIManager properties for ToggleButton at all, or it at least does not supply them itself (there is a quick example online to find all property keys from the UIManager, which in the example is conveniently limited explicitly to Color properties).

I have tried setting the background color:

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);

Not only does it not change the selected state, but that does not even effect the unselected state.

I have tried changing the background color only after receiving the action:

@Override
public void actionPerformed(ActionEvent e)
{
    JToggleButton button = (JToggleButton)e.getSource();
    if (button.isSelected()) // alternatively, (Boolean)getValue(Action.SELECTED_KEY)
    {
        button.setBackground(Color.RED);
    }
}

None of that works. The only thing that I have found to work requires me to draw the button myself in the selected state (which leads to a working example, albeit non-standard looking):

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);
        }
    }
}

That is slightly modified from a comment in this Java bug report. Interestingly (amusingly?), in claims to have been fixed in 1998.

Does anyone know of a better, L&F independent way to set the background color of a selected JToggleButton?

Budbudapest answered 27/4, 2011 at 17:21 Comment(3)
you can override BasicbuttonUI or just MetalButtonUI (then just for Metal L&F) some hint #5751811Tournai
why? the LAF is supposed to control the look, better let it do it's duty - if the succeed with interfering, your custom paint/color will stick out like a sore thumb in allLorilee
@Lorilee I usually agree that leaving the L&F is good practice, but simply, this is a desired feature. In particular, it's actually meant to stick out.Budbudapest
H
3

You might see if setIcon() is sufficient for your purpose, but you can also override paint() in the ButtonUI delegate.

Addendum: @kleopatra's comment is well-taken: changing the UI delegate is not trivial. @mKorbel's recent example shows both the difficulty and versatility of the approach. Its essential advantage is look & feel independence.

Some less ambitious approaches are mentioned here.

Hindemith answered 27/4, 2011 at 17:52 Comment(4)
re: implement a custom ButtonUI/subclass an existing - sure, everything is possible - but not everything is feasible ;-) Strong "No" from me.Lorilee
@kleopatra: I appreciate your comment; I've tried to put this in perspective, above.Hindemith
@ Jeanette I always appreciate your kick between my eyes, because it was always about (my) what is poppycock, but OP may not want to hear it isn't possible, wait for patch from BugsParade, then it tends to reply you *** create CustomComponents, if you are not satisfied with options setForeground or set IconTournai
I extended BasicToggleButtonUI and reimplemented the logic to draw everything, except with the proper background color. However, the lack of true portability (it would all just look right in my XP theme) makes it practically worthless especially given the significant added complexity (in terms of hidden bugs), not to mention that users must call toggleButton.setUI(myImpl). I am going to stay with the final option I listed (extend and draw manually if selected) and hope that future versions of the JDK actually support the background property. It's not gorgeous, but it does the job.Budbudapest
L
8
JToggleButton btn = new JToggleButton(...);
btn.setUI(new MetalToggleButtonUI() {
    @Override
    protected Color getSelectColor() {
        return Color.RED;
    }
});
Literalism answered 30/3, 2014 at 3:7 Comment(0)
R
6

"ToggleButton.selected" is wrong, it require "ToggleButton.select". And should be update to the component.

UIManager.put("ToggleButton.select", Color.WHITE);
SwingUtilities.updateComponentTreeUI(togglebuttonname);
Redingote answered 5/3, 2013 at 4:38 Comment(3)
That's a great catch and this does work! However, there is a rather large caveat that it globally effects all JToggleButtons unless you create your own instance and provide a new static ComponentUI createUI(JComponent c) for it. As a result, I am still stuck rolling my own, but for those that want to globally change the background color, then your approach is a working solution.Budbudapest
I was too excited, and the spelling mistake is a good catch. However, this does not work in the Windows system look and feel.Budbudapest
Looking into it even further, it appears that the system L&F does not even use any of the ToggleButton keys that are associated with Color (e.g., "ToggleButton.background").Budbudapest
H
3

You might see if setIcon() is sufficient for your purpose, but you can also override paint() in the ButtonUI delegate.

Addendum: @kleopatra's comment is well-taken: changing the UI delegate is not trivial. @mKorbel's recent example shows both the difficulty and versatility of the approach. Its essential advantage is look & feel independence.

Some less ambitious approaches are mentioned here.

Hindemith answered 27/4, 2011 at 17:52 Comment(4)
re: implement a custom ButtonUI/subclass an existing - sure, everything is possible - but not everything is feasible ;-) Strong "No" from me.Lorilee
@kleopatra: I appreciate your comment; I've tried to put this in perspective, above.Hindemith
@ Jeanette I always appreciate your kick between my eyes, because it was always about (my) what is poppycock, but OP may not want to hear it isn't possible, wait for patch from BugsParade, then it tends to reply you *** create CustomComponents, if you are not satisfied with options setForeground or set IconTournai
I extended BasicToggleButtonUI and reimplemented the logic to draw everything, except with the proper background color. However, the lack of true portability (it would all just look right in my XP theme) makes it practically worthless especially given the significant added complexity (in terms of hidden bugs), not to mention that users must call toggleButton.setUI(myImpl). I am going to stay with the final option I listed (extend and draw manually if selected) and hope that future versions of the JDK actually support the background property. It's not gorgeous, but it does the job.Budbudapest
N
3

When it using "Windows look and feel"; in Netbeans you have to do only two things.

  1. Go to properties
  2. Un-select 'contentAreaFilled'
  3. Select 'opaque'
  4. You can set a background color in properties or by hard coding

In other way,

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

That's all.:-)

Nonaggression answered 3/11, 2019 at 7:2 Comment(0)
H
1

You can simply force background color before each repaint - for that you have to alter paintComponent, check if button is toggled, set background depending on toggle state and, at last, let super class do the actual paint job:

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);
    }
}
Heldentenor answered 25/11, 2016 at 11:29 Comment(1)
Doesn't work for me. Swing doesn't use the supplied background color if the button is selected. This works with normal JButtons but you have to implement the select/deselect logic by yourself (which is very easy).Gent
G
0

If you would rather use an action listener instead of overriding methods in a UI you can just change the UI to a UI without any selectColor properties.

Here is an example I used recently

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);
                }
            }
        });
    }
}
Gypsum answered 1/12, 2016 at 14:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.