Java Swing持久弹出窗口

Java Swing持久弹出窗口,java,swing,mouseevent,jpopupmenu,jpopup,Java,Swing,Mouseevent,Jpopupmenu,Jpopup,我需要显示一个带有自定义组件的swing弹出窗口。弹出窗口应该保持可见,直到我自己隐藏它,但不应该获得焦点 我有一个由其他开发人员编写的代码,它以以下方式执行: popupMenu = new JPopupMenu(); popupMenu.add(myCustomComponent, BorderLayout.CENTER); popupMenu.setFocusable(false); popupMenu.setVisible(true

我需要显示一个带有自定义组件的swing弹出窗口。弹出窗口应该保持可见,直到我自己隐藏它,但不应该获得焦点

我有一个由其他开发人员编写的代码,它以以下方式执行:

       popupMenu = new JPopupMenu();
       popupMenu.add(myCustomComponent, BorderLayout.CENTER);
       popupMenu.setFocusable(false);
       popupMenu.setVisible(true);
       popupMenu.show(parentComponent, x, y);
这似乎是可行的,但有一个错误-当弹出窗口可见时,组件外的第一次鼠标单击将被弹出窗口使用。因此,我需要单击两次以将焦点设置为另一个组件

我怎样才能修好它?或者,弹出窗口的正确方式是什么

更新

最后,我设法用简短的代码片段重现了我的问题。感谢纪尧姆·波尔特给了我一个起点

代码如下:

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();
            }
        });
    }
}
导入java.awt.BorderLayout;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入javax.swing.*;
公共类TestJPopup{
受保护的void initUI(){
JFrame=newjframe(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
最终JTextField textField=新的JTextField(“某些文本字段”);
frame.add(textField,BorderLayout.WEST);
最终JButton buttonothit=新JButton(“击中我”);
addActionListener(新建ActionListener()){
已执行的公共无效操作(操作事件e){
showMessageDialog(buttonToHit,“您成功地点击了按钮”);
}
});
frame.add(buttonothit);
框架设置尺寸(200100);
frame.setVisible(true);
最终JPopupMenu弹出窗口=新建JPopupMenu();
添加(新的JLabel(“嘿!
我是弹出窗口!”), 边界布局(北面); popup.setFocusable(false); popup.setVisible(true); 显示(文本字段,60,60); //我想在用户单击文本字段时激活弹出窗口 addMouseListener(新的MouseAdapter(){ @凌驾 公共无效mouseClicked(MouseEvent e){ 如果(弹出!=null){ 显示(文本字段,60,60); } } }); } 公共静态void main(字符串[]args)引发异常{ Class lnfClass=Class.forName(“com.sun.java.swing.plaf.windows.WindowsLookAndFeel”,true, Thread.currentThread().getContextClassLoader()); LookAndFeel=(LookAndFeel)lnfClass.newInstance(); UIManager.setLookAndFeel(feel); SwingUtilities.invokeLater(新的Runnable(){ 公开募捐{ 新的TestJPopup().initUI(); } }); } }
两个关键时刻:

  • 使用的Windows外观(默认不可复制)
  • 鼠标侦听器连接到主框架中的文本字段

    • 这不是答案,只是一个我目前无法重现您描述的行为的例子。也许从这段代码开始,试着用修改过的非工作代码重现错误和编辑文章

      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();
                  }
              });
          }
      }
      
      导入java.awt.BorderLayout;
      导入java.awt.event.ActionEvent;
      导入java.awt.event.ActionListener;
      导入javax.swing.JButton;
      导入javax.swing.JFrame;
      导入javax.swing.JLabel;
      导入javax.swing.JOptionPane;
      导入javax.swing.jpopmenu;
      导入javax.swing.JTextField;
      导入javax.swing.SwingUtilities;
      公共类TestJPopup{
      受保护的void initUI(){
      JFrame=newjframe(TestJPopup.class.getSimpleName());
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      JLabel leftLabel=新的JLabel(“左”);
      frame.add(leftLabel,BorderLayout.WEST);
      最终JButton buttonothit=新JButton(“击中我”);
      addActionListener(新建ActionListener()){
      @凌驾
      已执行的公共无效操作(操作事件e){
      showMessageDialog(buttonToHit,“您成功地点击了按钮”);
      }
      });
      frame.add(buttonothit);
      框架。设置尺寸(500400);
      frame.setVisible(true);
      JPopupMenu=新的JPopupMenu();
      popupMenu.add(新的JLabel(“为模拟您的自定义组件而制作的自定义组件”),
      边界布局(北面);
      JTextField textfield=新的JTextField(30);
      弹出菜单。添加(文本字段);
      popupMenu.setFocusable(假);
      popupMenu.setVisible(真);
      显示(leftLabel,20,20);
      //让我们强制焦点位于弹出菜单中的组件中
      textfield.requestFocusInWindow();
      }
      公共静态void main(字符串[]args){
      SwingUtilities.invokeLater(新的Runnable(){
      @凌驾
      公开募捐{
      新的TestJPopup().initUI();
      }
      });
      }
      }
      
      不是解决方案,而是:

      在我看来,这就像一个bug,即使是一个普通的组件弹出窗口也会表现出相同的错误行为(在winLAF和Nimbus中,而不是在金属中):


      用于快速研究和/或未来读者

      • 本期可复制,并提交给

        (a)

        (b)

      • jdk1.6.0_25
        jdk1.7.0_04
        上测试

      • 关于
        WinXp
        Win7
        的相同问题

      • 对于
        Look and Feel
        systemlook and Feel
        /
        windowslook and Feel


      这里有一个可能的解决方法,它是使用
      JWindow
      而不是mKorbel在评论中提出的
      jpopmenu

      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();
                  }
              });
          }
      }
      
      import java.awt.*;
      导入java.awt.event.*;
      导入javax.swing.*;
      公共类TestJPopup{
      受保护的void initUI(){
      final JFrame=newjframe(TestJPopup.class.getSimpleName());
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      最终JTextField textField=新的JTextField(“某些文本字段”);
      frame.add(textField,BorderLayout.WEST);
      最终按钮按钮
      
      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();
                  }
              });
          }
      }
      
      UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
      
              // Ask UIManager about should we consume event that closes
              // popup. This made to match native apps behaviour.