MouseEvent 在 JScrollPane 中丢失

2022-01-24 00:00:00 java swing jpanel jscrollpane jframe

这是我用来显示我在另一个项目中面临的问题的代码.

This is the code I am using to show the issue which I am facing in another project.

如果我使用 JScrollPane 作为 panel2 的包装器,我不会得到任何这样的行.为什么?我想点击 JscrollPane 并打印如下事件.

I am not getting any line like this if I use JScrollPane as a wrapper for panel2. Why? I want to click on JscrollPane and got event printed as following.

java.awt.event.MouseEvent[MOUSE_CLICKED,(800,469),absolute(808,499),button=1,modifiers=Button1,clickCount=1] on javax.swing.JPanel[,0,0,934x612,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.LineBorder@cc0e01,flags=9,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=630]]

如果我现在改变

panel1.add(pane);

panel1.add(panel2);

然后上面的消息就打印出来了.

Then the message above got printed.

public class LostMouseEvent {

public static void main(String[] args) {
    new LostMouseEvent();
}

public LostMouseEvent() {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());

            JPanel panel1 = new JPanel();
            JPanel panel2 = new JPanel();
            JScrollPane pane = new JScrollPane(panel2);

            panel1.setPreferredSize(new Dimension(880, 630));
            panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
            panel2.setPreferredSize(new Dimension(840, 610));
            panel2.setBorder(BorderFactory.createLineBorder(Color.green));


            panel1.add(pane);
            frame.add(panel1);

            frame.pack();
            frame.setVisible(true);
            frame.setSize(950, 650);

            panel1.addMouseListener(new MyMouseListener());
        }
    });
}

private class MyMouseListener extends MouseAdapter {
    @Override
    public void mouseClicked (MouseEvent me) {
        System.out.println(me);
    }
}
}

UPD:事实上,在我的项目中不止一个 panel2.最初,我有 panel1 和许多 panel2 里面.然后我想用 JScrollPane 包装每个 panel2 并开始面对这个问题.

UPD: In fact in my project there is more than just one panel2. Originally, I had panel1 and many panel2 inside. Then I wanted to wrap each panel2 with JScrollPane and started to face this problem.

我只需要一个 MouseListener 就可以最大限度地减少对代码的更改.

I need to have only one MouseListener to minimize changes to the code.

推荐答案

  • 使用 EDT 创建和操作 Swing 组件
  • 在设置 JFrame 可见之前不要调用 setSize() 而是调用 pack().
  • 不要调用 setPrefferedSize() 而是覆盖 getPrefferedSize()
    • Use EDT for creation and manipulation of Swing components
    • Dont call setSize() rather call pack() before setting JFrame visible.
    • Dont call setPrefferedSize() rather override getPrefferedSize()
    • 您的代码按预期工作,只有在单击 panel1 时才会打印消息,请注意 panel1JScrollPane 后面,因此外部的任何内容绿色边框是 panel1.要使其适用于 JScrollpane/panel2JPanel/panel1 只需添加 MouseListener 到 BOTH 的必需组件:

      Your code works as expected, it will only print the message if panel1 is clicked, note panel1 is behind JScrollPane, thus anything outside the green border is panel1. To make it work for both the JScrollpane/panel2 and JPanel/panel1 simply add the MouseListener to BOTH of the required components:

      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Dimension;
      import java.awt.EventQueue;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;
      import java.awt.event.MouseListener;
      import javax.swing.BorderFactory;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JScrollPane;
      import javax.swing.SwingUtilities;
      
      public class LostMouseEvent {
      
          public static void main(String[] args) {
              SwingUtilities.invokeLater(new Runnable() {
      
                  @Override
                  public void run() {
              new LostMouseEvent();
                  }
              });
          }
      
          public LostMouseEvent() {
              EventQueue.invokeLater(new Runnable() {
                  @Override
                  public void run() {
                      JFrame frame = new JFrame();
                      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                      frame.setLayout(new BorderLayout());
      
                      JPanel panel1 = new JPanel() {
      
                          @Override
                          public Dimension getPreferredSize() {
                              return new Dimension(880, 630);
                          }
      
                      };
                      JPanel panel2 = new JPanel() {
      
                          @Override
                          public Dimension getPreferredSize() {
                              return new Dimension(840, 610);
                          }
                      };
                      JScrollPane pane = new JScrollPane(panel2);
      
                      panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
                      panel2.setBorder(BorderFactory.createLineBorder(Color.green));
      
      
                      panel1.add(pane);
                      frame.add(panel1);
      
                      MouseListener ml=new MyMouseListener();
      
                      //add mouse listener to panel1 and panel2
                      panel1.addMouseListener(ml);
                      panel2.addMouseListener(ml);
      
                      //alternatively add to pane
                      //pane.addMouseListener(ml);
      
                      frame.pack();
                      frame.setVisible(true);
      
                  }
              });
          }
      
          private class MyMouseListener extends MouseAdapter {
      
              @Override
              public void mouseClicked(MouseEvent me) {
                  System.out.println(me);
              }
          }
      }
      

      不过,我个人不建议这样做,

      I personally would not recommend this, however,

      要向 JFrame 添加单个侦听器以捕获所有 MouseEvent,请使用 Toolkit 类并调用 addAWTEventListener 像这样:

      To add a single listener to the JFrame that will capture all MouseEvents use Toolkit class and call addAWTEventListener like so:

      Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
          @Override
          public void eventDispatched(AWTEvent awte) {//all mouse events will be processed here you will have to check for the mouse events you are interested in
          System.out.println(awte);
          }
      }, AWTEvent.MOUSE_EVENT_MASK);//for Mouse events only
      

      更新 1:

      您还可以通过 JFrame.getGlassPane().addMouseListener(ml)<将 MouseListener 添加到您的 JFrame 的 glasspane/code> 设置 JFrame 可见后,不要忘记设置 glasspane 可见.这将允许您只需要添加一个 Listener.见这里:

      You could also add the MouseListener to your JFrames glasspane via JFrame.getGlassPane().addMouseListener(ml) dont forget to set the glasspane visible after setting JFrame visible. This will allow you to only have to add a single Listener. See here:

      ...
      
      MouseListener ml = new MyMouseListener();
      
      //add mouse listener to panel1 and panel2
      //panel1.addMouseListener(ml);
      //panel2.addMouseListener(ml);
      
      //alternatively add to pane
      //pane.addMouseListener(ml);
      
      frame.getGlassPane().addMouseListener(ml);
      
      frame.pack();
      frame.setVisible(true);
      
      frame.getGlassPane().setVisible(true);
      
      ...
      

      UPADTE 2:

      MouseEventJScrollPane 中丢失的主要原因是因为它是一个错误.请参阅此处.

      The main reason for you having the problem of the MouseEvent getting lost in JScrollPane is because its a bug. See here.

      显示的解决方法是:

      public Test()
        {
          setUI(new javax.swing.plaf.metal.MetalScrollPaneUI(){
            public void installListeners(JScrollPane scrollPane){}
          });
          JPanel canvas = new JPanel();
          canvas.add( new JLabel("Test") );
      
          setViewportView( canvas );
          setVisible(true);
        }
      

相关文章