Swing: show / hide mouse button

So, I want to put the button in JPanel , but I want it to be invisible / hidden if the mouse does not hover over it. At this point, the button should be visible, responsive to clicks, etc. When the mouse leaves the area, it must again be hidden.

I tried adding a MouseListener to my JButton and using setVisible() , but when I hide the ( setVisible(false) ) button, the listener no longer works - the application behaves as if the button is not there at all.

What is the correct way to implement this behavior?

Edit: I am using an absolute layout ( setLayout(null) ), and I manually place my component using setBounds(x, y, width, height) .

+6
source share
5 answers

Use the icons to show (color) or hide (transparently) the button, respectively.

enter image description here

 import java.awt.*; import java.awt.image.BufferedImage; import javax.swing.*; class InvisiButton { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { int size = 30; JPanel gui = new JPanel(new GridLayout(4,10,4,4)); for (int ii=0; ii<40; ii++) { JButton b = new JButton(); b.setContentAreaFilled(false); b.setIcon(new ImageIcon( new BufferedImage(size,size,BufferedImage.TYPE_INT_RGB))); b.setRolloverIcon(new ImageIcon( new BufferedImage(size,size,BufferedImage.TYPE_INT_ARGB))); b.setBorder(null); gui.add(b); } JOptionPane.showMessageDialog(null, gui); } }; // Swing GUIs should be created and updated on the EDT // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html SwingUtilities.invokeLater(r); } } 
+7
source

One approach is to give the button no text, no border, no blank icon to match the actual rollover icon.

Appendix: This updated example is based on the @Andrew short blank icon that displays here .

 import java.awt.EventQueue; import java.awt.GridLayout; import java.awt.image.BufferedImage; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; /** @see https://stackoverflow.com/a/14410594/230513 */ public class RollButton { private static final int N = 64; private void display() { JFrame f = new JFrame("RollButton"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel p = new JPanel(new GridLayout()); p.setBorder(BorderFactory.createEmptyBorder(N, N, N, N)); p.add(createButton(UIManager.getIcon("OptionPane.errorIcon"))); f.add(p); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } private JButton createButton(Icon icon) { JButton b = new JButton(); b.setBorderPainted(false); b.setText(""); // /questions/935461/swing-showhide-button-on-mouse-over/3294176#3294176 b.setIcon(new ImageIcon(new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB))); b.setRolloverEnabled(true); b.setRolloverIcon(icon); return b; } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new RollButton().display(); } }); } } 
+4
source

seems to work just fine like CardLayout (a button with an empty label)

 import java.awt.*; import javax.swing.*; import java.awt.event.*; class Testing { JButton btn = new JButton("Click Me"); JLabel lbl = new JLabel(); CardLayout cl = new CardLayout(); JPanel cardLayoutPanel = new JPanel(cl); public void buildGUI() { lbl.setPreferredSize(btn.getPreferredSize()); lbl.setBorder(BorderFactory.createLineBorder(Color.black));//testing size, remove later cardLayoutPanel.add(lbl,"lbl"); cardLayoutPanel.add(btn,"btn"); JPanel p = new JPanel(new GridBagLayout()); p.add(cardLayoutPanel,new GridBagConstraints()); JFrame f = new JFrame(); f.getContentPane().add(p); f.setSize(400,300); f.setLocationRelativeTo(null); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); MouseListener listener = new MouseAdapter(){ public void mouseEntered(MouseEvent me){ if(me.getSource() == lbl) cl.show(cardLayoutPanel,"btn"); } public void mouseExited(MouseEvent me){ if(me.getSource() == btn) cl.show(cardLayoutPanel,"lbl"); } }; lbl.addMouseListener(listener); btn.addMouseListener(listener); btn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae){ System.out.println("me clicked"); } }); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable(){ public void run(){ new Testing().buildGUI(); } }); } } 
+4
source

You must deal with mouse events on JPanel . Get the mouse position on the JPanel and see if it is within the JButton .

While most LayoutManager ignores invisible components, you cannot always get the borders of a button when it's hidden - thanks to MadProgrammer. You must add an additional component to save the "place" - for example, use JPanel:

 JPanel btnContainer = new JPanel(new BorderLayout()); // use BorderLayout to maximize its component btnContainer.add(button); // make button the only component of it panel.add(btnContainer); // panel is the JPanel you want to put everything on panel.addMouseMotionListener(new MouseAdapter() { public void mouseMoved (MouseEvent me) { if (btnContainer.getBounds().contains(me.getPoint())) { // the bounds of btnContainer is the same as button to panel button.setVisible(true); // if mouse position on JPanel is within the bounds of btnContainer, then make the button visible } else { button.setVisible(false); } } }); button.addMouseLisener(new MouseAdapter() { public void mouseExited (MouseEvent me) { // after thinking about it, I think mouseExited() is still needed on button -- because // if you move your mouse off the button very quickly and move it out of panel bounds, // before panel captures any mouse move event, button will stay visible button.setVisible(false); // And now, it will hide itself. } }); 

And there is another way to "simulate" an invisible button. You can override the paint() method of the JButton class, clear the empty rectangle if "invisible":

 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Demo extends JFrame { class MyButton extends JButton { private boolean show; public MyButton (String text) { // You can implement other constructors like this. super(text); } @Override public void paint (Graphics g) { if (show) { super.paint(g); } else { g.setBackground(panel.getBackground()); g.clearRect(0, 0, getWidth(), getHeight()); } } public void setShow (boolean show) { // make a different name from setVisible(), use this method to "fakely" hide the button. this.show = show; repaint(); } } private MyButton btn = new MyButton("Yet another button"); private JPanel panel = new JPanel(new BorderLayout()); public Test () { setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(500, 500); setLocationRelativeTo(null); btn.setShow(false); btn.setPreferredSize(new Dimension(100, 100)); btn.addMouseListener(new MouseAdapter() { // capture mouse enter and exit events of the button, simple! public void mouseExited (MouseEvent me) { btn.setShow(false); } public void mouseEntered (MouseEvent me) { btn.setShow(true); } }); panel.add(btn, BorderLayout.NORTH); add(panel); setVisible(true); } } 
+3
source

MouseEvent (or other input events) will be fired only when the component is actually visible.

Another problem is that the layout manager will probably ignore it when it does the layout, making the button (possibly) width and height 0x0 ...

You can add a button to the user panel (using BorderLayout ), override the getPreferredSize panel and return the preferred button size. This will allow the layout manager of the panel layout, but allows you to put on a button.

It can also be used to catch mouse events on behalf of a button.

nb After some thought, the above will not work. After the button becomes visible, the mouseExit event will be mouseExit , which will cause the panel to hide the button. As soon as the button becomes visible, it will also begin to consume mouse events, which means that it would be impossible to tell when the mouse went outside the area. Of course, you can use a bunch of if statements and flags to determine what happens, but there are simple ways ...

Update

Another approach is to create your own custom button and overriding the paint method, you can fool the component in a transparent way, but still receive notifications about mouse events and get the benefits of layout managers.

 public class TestButton02 { public static void main(String[] args) { new TestButton02(); } public TestButton02() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { setLayout(new GridBagLayout()); TestButton btn = new TestButton("Testing"); btn.setBoo(false); add(btn); } } public class TestButton extends JButton { private boolean boo; public TestButton(String text) { super(text); addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { setBoo(true); } @Override public void mouseExited(MouseEvent e) { setBoo(false); } }); } public void setBoo(boolean value) { if (boo != value) { boo = value; repaint(); } } public boolean isBoo() { return boo; } @Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); g2d.setComposite(AlphaComposite.SrcOver.derive(isBoo() ? 1f : 0f)); super.paint(g2d); g2d.dispose(); } } } 

This is basically a special flag that acts on AlphaComposite to draw a button transparent or opaque ...

+2
source

All Articles