所以我想在JPanel
中放一个按钮,但我想让它保持不可见/隐藏,除非鼠标指针悬停在它上面。此时,应使按钮可见,对点击作出反应等。当鼠标离开该区域时,它应该再次隐藏。
我尝试向MouseListener
添加JButton
并使用setVisible()
,但是当我隐藏按钮(setVisible(false)
)时,监听器不再起作用了 - 应用程序的行为就像按钮根本不存在一样。
实现此行为的正确方法是什么?
编辑:我使用的是绝对布局(setLayout(null)
),我使用setBounds(x, y, width, height)
手动放置我的组件。
答案 0 :(得分: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);
}
}
答案 1 :(得分:4)
一种方法是给按钮没有文字,没有边框,以及一个大小匹配实际翻转图标的空图标。
附录:这个更新的例子依赖于@ Andrew的简洁空图标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("");
// 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();
}
});
}
}
答案 2 :(得分: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();
}
});
}
}
答案 3 :(得分:3)
您应该在JPanel
处理鼠标事件。获取JPanel
上的鼠标位置,看看它是否在JButton
的范围内。
虽然大多数LayoutManager
会忽略不可见的组件,但是当隐藏按钮时,你不能总是得到按钮的界限 - 感谢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);
}
}
答案 4 :(得分: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
对按钮进行透明或不透明的绘制......