我需要使用自定义组件显示swing弹出窗口。弹出窗口应保持可见,直到我自己隐藏它,但不应该得到焦点。
我有一些其他开发人员编写的代码,它按以下方式执行:
popupMenu = new JPopupMenu();
popupMenu.add(myCustomComponent, BorderLayout.CENTER);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(parentComponent, x, y);
这似乎有效,但有一个错误 - 当弹出窗口可见时,弹出窗口会消耗组件外部的第一次鼠标点击。所以我需要点击两次将焦点设置到另一个组件。
我该如何解决?或者制作弹出窗口的正确方法是什么?
更新
最后我设法用短代码片段重现了我的问题。感谢Guillaume Polet为我提供了一个起点。
以下是代码:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 100);
frame.setVisible(true);
final JPopupMenu popup = new JPopupMenu();
popup.add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setFocusable(false);
popup.setVisible(true);
popup.show(textField, 60, 60);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (popup != null) {
popup.show(textField, 60, 60);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
两个关键时刻:
答案 0 :(得分:4)
不是答案,而只是一个示例SSCCE,我目前无法重现您描述的行为。也许从这段代码开始,尝试重现错误并使用修改后的非工作代码编辑帖子。
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel leftLabel = new JLabel("Left");
frame.add(leftLabel, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(500, 400);
frame.setVisible(true);
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JLabel("<html>A Custom<br>component<br>made to<br> simulate <br>your custom component</html>"),
BorderLayout.NORTH);
JTextField textfield = new JTextField(30);
popupMenu.add(textfield);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(leftLabel, 20, 20);
// Let's force the focus to be in a component in the popupMenu
textfield.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestJPopup().initUI();
}
});
}
}
答案 1 :(得分:2)
不是解决方案,但是:
对我来说看起来像个错误,即使是普通的componentPopup也表现出同样的错误行为(在winLAF和Nimbus中,而不是在Metal中):
JTextField field = new JTextField("some popup owner");
JPopupMenu menu = new JPopupMenu();
menu.add("dummy");
field.setComponentPopupMenu(menu);
Action action = new AbstractAction("hit me!") {
@Override
public void actionPerformed(ActionEvent e) {
LOG.info("got hit!");
}
};
JComponent content = new JPanel();
content.add(new JButton(action));
content.add(field);
答案 2 :(得分:2)
快速研究和/或为未来的读者,
答案 3 :(得分:2)
以下是评论中 mKorbel 提出的JWindow
而不是JPopupMenu
的可能解决方法:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
final JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 70);
frame.setVisible(true);
final JWindow popup = new JWindow();
popup.getContentPane().add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.pack();
popup.setFocusable(false);
popup.setVisible(true);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (popup != null) {
popup.setVisible(true);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.toFront();
}
}
});
textField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.setVisible(false);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
答案 4 :(得分:0)
以下是解决问题的神奇之处:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
在查看BasicPopupMenuUI类的源代码后,我发现了这一点。显然,根据代码中的以下注释,这种行为是一种深思熟虑的设计选择,但它对我来说确实感觉像是一个错误。
// Ask UIManager about should we consume event that closes // popup. This made to match native apps behaviour.
顺便说一句,它也发生在Java 5和6中。