18

I want to have custom colors according to the mouse events (mouse enter, exit, pressed, etc). So to accomplish this, I wrote the code below. It is fine for everything, except in the case of the mouse pressed event, which does nothing.

It only works if I override the color in the UIManager like this UIManager.put("Button.select", Color.red);. The problem with the UIManager is that it will change for all my buttons.

Can anyone tell me what I might be doing wrong or what is the best approach to accomplish what I'm trying to do?

My Code:

final JButton btnSave = new JButton("Save");

btnSave.setForeground(new Color(0, 135, 200).brighter());
btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
btnSave.setBorder(null);
btnSave.setBackground(new Color(3, 59, 90));

btnSave.addMouseListener(new MouseListener() {
    @Override
    public void mouseReleased(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // Not working :(
        btnSave.setBackground(Color.pink);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }
});

Edit1: So, instead of MouseListener, I'm using ChangeListener and ButtonModel as suggested by mKorbel. With this code I'm still not observing any changes on mouse pressed in the button except when I press and drag outside the button. Any thoughts?

btnSave.getModel().addChangeListener(new ChangeListener() {

    @Override
    public void stateChanged(ChangeEvent e) {
        ButtonModel model = (ButtonModel) e.getSource();

        if (model.isRollover()) {
            btnSave.setBackground(new Color(3, 59, 90).brighter());
        } else if (model.isPressed()) {
            btnSave.setBackground(Color.BLACK);
        } else {
            btnSave.setBackground(new Color(3, 59, 90));
        }
    }
});
Jasperan
  • 2,154
  • 1
  • 16
  • 40
ktulinho
  • 3,870
  • 9
  • 28
  • 35

9 Answers9

29

The problem is caused by the fact that a JButton has its content area filled by default and that the Metal L&F will automatically fill it with its internal chosen color when button is pressed.

Best thing to do, is to extend JButton to create your own button, disable content area filled, and paint yourself the background of the button.

Here is a small demo for that (not sure it works on other L&F, even pretty sure it does not):

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class TestButton {

    class MyButton extends JButton {

        private Color hoverBackgroundColor;
        private Color pressedBackgroundColor;

        public MyButton() {
            this(null);
        }

        public MyButton(String text) {
            super(text);
            super.setContentAreaFilled(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            if (getModel().isPressed()) {
                g.setColor(pressedBackgroundColor);
            } else if (getModel().isRollover()) {
                g.setColor(hoverBackgroundColor);
            } else {
                g.setColor(getBackground());
            }
            g.fillRect(0, 0, getWidth(), getHeight());
            super.paintComponent(g);
        }

        @Override
        public void setContentAreaFilled(boolean b) {
        }

        public Color getHoverBackgroundColor() {
            return hoverBackgroundColor;
        }

        public void setHoverBackgroundColor(Color hoverBackgroundColor) {
            this.hoverBackgroundColor = hoverBackgroundColor;
        }

        public Color getPressedBackgroundColor() {
            return pressedBackgroundColor;
        }

        public void setPressedBackgroundColor(Color pressedBackgroundColor) {
            this.pressedBackgroundColor = pressedBackgroundColor;
        }
    }

    protected void createAndShowGUI() {
        JFrame frame = new JFrame("Test button");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final MyButton btnSave = new MyButton("Save");
        btnSave.setForeground(new Color(0, 135, 200).brighter());
        btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
        btnSave.setBorder(null);
        btnSave.setBackground(new Color(3, 59, 90));
        btnSave.setHoverBackgroundColor(new Color(3, 59, 90).brighter());
        btnSave.setPressedBackgroundColor(Color.PINK);
        frame.add(btnSave);
        frame.setSize(200, 200);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestButton().createAndShowGUI();
            }
        });

    }

}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
11
Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • _never, don't to use MouseListener for JButton_ I have never used. But can you tell me why? – Amarnath Jan 31 '13 at 14:16
  • all those methods are implemented in `ButtonsComponents APIS` (JButton throught JMenuItem, to JRadioButton), derived `Mouse` & `Keys Events` in `ButtonModel`, safe, directly, confortly, accesible for all L&Fs (I know two three exceptions, not important themes in L&Fs) :-) dot :-) – mKorbel Jan 31 '13 at 14:19
  • 1
    +1 too, indeed using ButtonModel is a better way to go. @Che, it's always better to rely on a higher model abstraction such as ButtonModel, than relying on low-level API's such as MouseListener. – Guillaume Polet Jan 31 '13 at 14:19
  • @GuillaumePolet +1. But how guys like me will know these things while working. If I am in this situation I will just go for _MouseListener_ and finish my job. My question is, how can/shall I know what is best/better? – Amarnath Jan 31 '13 at 14:25
  • @Che answer to your question is in comment by (@GuillaumePolet) `higher model abstraction` v.s. `low-level` – mKorbel Jan 31 '13 at 14:33
  • @mKorbel I have seen that. So you mean to say use all the `higher level abstraction classes` where ever possible? – Amarnath Jan 31 '13 at 14:35
  • So, I'm trying to follow the suggested approach of using changeListener + ButtonModel, but I'm not getting any color change when I press the button. The only time I observe the changes is when I click and drag the mouse outside the button. am I missing something? See the code in the original question under the edit1. – ktulinho Jan 31 '13 at 14:43
  • @ktulinho I haven't any issue with answer by [Guillaume Polet, there is everything that you'll be needed](http://stackoverflow.com/a/14627771/714968) – mKorbel Jan 31 '13 at 14:46
6
public class MyCustomButton extends JButton {
    private Color pressedColor = Color.GREEN;
    private Color rolloverColor = Color.RED;
    private Color normalColor = Color.BLUE;

    public MyCustomButton (String text) {
        super(text);
        setBorderPainted(false);
        setFocusPainted(false);

        setContentAreaFilled(false);
        setOpaque(true);

        setBackground(normalColor);
        setForeground(Color.WHITE);
        setFont(new Font("Tahoma", Font.BOLD, 12));
        setText(text);

        addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent evt) {
                if (getModel().isPressed()) {
                    setBackground(pressedColor);
                } else if (getModel().isRollover()) {
                    setBackground(rolloverColor);
                } else {
                    setBackground(normalColor);
                }
            }
        });
    }
}
Marcel
  • 1,494
  • 1
  • 23
  • 33
1

Try this with what you already have:

yourButton.setBorder(null);
yourButton.setContentAreaFilled(false);
ASGM
  • 11,051
  • 1
  • 32
  • 53
1

In my case, I just wanted a simple background and color switch when the user pressed the button.

Adapted Guillaume Polet's solution:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.ButtonModel;
import javax.swing.JButton;

@SuppressWarnings("serial")
class Button extends JButton {

    private Color pressedForeground = Color.BLACK;
    private Color pressedBackground = Color.WHITE;

    Button() {
        this(null);
    }

    Button(String text) {
        super(text);
        super.setContentAreaFilled(false);
        setForeground(Color.WHITE);
        setBackground(Color.BLACK);
        setFocusPainted(false);
        setBorderPainted(false);
        setFont(new Font("arial", Font.PLAIN, 16));
    }

    @Override
    public void paint(Graphics g) {
        Color oldFg = getForeground();
        Color newFg = oldFg;
        ButtonModel mod = getModel();

        if (mod.isPressed()) {
            newFg = pressedForeground;
            g.setColor(pressedBackground);
        } else {
            g.setColor(getBackground());
        }

        g.fillRect(0, 0, getWidth(), getHeight());
        setForeground(newFg);
        super.paintComponent(g);
        setForeground(oldFg);
    }
}
Afonso Matos
  • 2,406
  • 1
  • 20
  • 30
0

Could be nothing but could try to instead use: Color.PINK, capital letters? Any change when doing that?

Also wouldn't the mousepressed & mouseclicked override each other? mousepressed should react when you press it & clicked when you release the pressing of the mouse

Random IT Guy
  • 625
  • 1
  • 8
  • 16
  • _Could be nothing but could try to instead use: Color.PINK, capital letters?_ It is nothing. They both represent the exact same Color. – Guillaume Polet Jan 31 '13 at 13:52
  • Color.PINK refers to Color.pink internally. Both are same. – Amarnath Jan 31 '13 at 13:53
  • Even so, wouldn't the mouseclicked event happen right after the mousepressed event? ==> mouseClicked(MouseEvent) Called just after the user clicks the listened-to component. ==> mousePressed(MouseEvent) Called just after the user presses a mouse button while the cursor is over the listened-to component. – Random IT Guy Jan 31 '13 at 13:58
  • No, events occurs in the following order: mouse pressed, possibly mouse drag, mouse release and then possibly mouse click (as long as button has not been released, there is no click event) – Guillaume Polet Jan 31 '13 at 14:00
0

Instead of setting the color because that appears not to work you could just try setting the background to a stretchable image file and set that as the background. Might that work?

0

This Works For Me! Try This Bro...

final JButton btnSave = new JButton("Save");

btnSave.setForeground(new Color(0, 135, 200).brighter());
btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
btnSave.setBorder(null);

UIManager.put("Button.select", new Color(3, 59, 90)); //<--- Added ---
//btnSave.setBackground(new Color(3, 59, 90));

btnSave.addMouseListener(new MouseListener() {
    @Override
    public void mouseReleased(MouseEvent e) {
        UIManager.put("Button.select", new Color(3, 59, 90)); //<--- Added ---
        //btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // it's working :)
        UIManager.put("Button.select", Color.pink); //<--- Added ---
        //btnSave.setBackground(Color.pink);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }
});    
0

Initialize a button

JButton button = new JButton();

Then, on mouse pressed do:

button.setContentAreaFilled(false);
button.setOpaque(true);
button.setBackground(/*Insert Color Here*/);

On mouse released do:

button.setContentAreaFilled(true);
button.setBackground(/*Reset Color Here*/);
Bradyo
  • 63
  • 7