6

所以我想在 a 中放一个按钮JPanel,但我想让它保持不可见/隐藏,除非鼠标指针悬停在它上面。此时,按钮应该是可见的,对点击做出反应等等。当鼠标离开该区域时,它应该再次被隐藏。

我尝试MouseListener在我的JButton和使用中添加 a setVisible(),但是当我隐藏按钮 ( setVisible(false)) 时,监听器不再工作 - 应用程序的行为就像按钮根本不存在一样。

实现此行为的正确方法是什么?

编辑:我正在使用绝对布局 ( setLayout(null)),并且我正在使用setBounds(x, y, width, height).

4

5 回答 5

7

使用图标分别显示(彩色)或隐藏(透明)按钮。

在此处输入图像描述

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);
    }
}
于 2013-01-19T02:39:16.540 回答
4

一种方法是给按钮没有文本、没有边框和一个空图标,其大小与真正的翻转图标相匹配。

附录:这个更新的例子依赖于@Andrew 的简洁的空图标

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("");
        // https://stackoverflow.com/a/14410597/230513
        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();
            }
        });
    }
}
于 2013-01-19T02:39:11.780 回答
4

似乎可以作为 CardLayout 工作(带有空标签的按钮)

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();
      }
    });
  }
}
于 2013-01-19T03:06:47.733 回答
3

您应该处理JPanel. 获取鼠标在 上的位置JPanel,看看它是否在JButton的范围内。

虽然大多数LayoutManagers 忽略了不可见的组件,所以当按钮被隐藏时你不能总是得到它的边界——感谢 MadProgrammer。您应该添加一个附加组件来保留“位置”——例如使用 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's bounds,
                                                 // before panel captures any mouse move event, button will stay visible
        button.setVisible(false);    // And now, it will hide itself.
    }
});

还有另一种“模拟”不可见按钮的方法。您可以覆盖类的paint()方法,JButton如果“不可见”清除一个空白矩形:

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);
    }
}
于 2013-01-19T02:11:29.897 回答
2

MouseEvent(或其他输入事件)只会在组件实际可见时触发。

你的另一个问题是布局管理器在布局时可能会忽略它,使按钮(可能)0x0宽度和高度......

您可以将按钮添加到自定义面板(使用BorderLayout),覆盖面板getPreferredSize并返回按钮的首选大小。这将允许布局管理器布局面板,但允许您将按钮放在上面。

这也可以用于代表按钮捕获鼠标事件。

nb经过一番思考,以上行不通。一旦按钮变得可见,mouseExit就会触发一个事件,这将触发面板隐藏按钮。一旦按钮变得可见,它也将开始使用鼠标事件,这意味着几乎不可能判断鼠标何时超出了窗格的范围。当然,您可以使用一堆if语句和标志来确定发生了什么,但是有一些简单的方法......

更新

另一种方法是为您自己创建一个自定义按钮,并通过覆盖该paint方法,您可以欺骗组件显示为透明,但仍会收到鼠标事件通知并获得布局管理器的好处。

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

    }

}

这基本上有一个特殊的标志,可以影响AlphaComposite将按钮绘制为透明或不透明......

于 2013-01-19T02:03:13.287 回答