【问题标题】:Use JScrollPane mouse listener over viewport components在视口组件上使用 JScrollPane 鼠标侦听器
【发布时间】:2012-07-13 18:09:18
【问题描述】:

我有一个 JScrollPane 可以将其 viewportView 设置为一系列不同的面板。每当单击其视口中的任何其他组件时,我都想获取 JScrollPane 组件。如果我将MouseListener 添加到 JScrollPane,当我直接单击窗格边框时它会接收我的鼠标事件,但当我单击组件时不会接收。

添加侦听器并最终找到封闭的滚动窗格的正确方法是什么?我不一定会提前知道我在视口中显示的面板上的所有组件 - 只是它们将位于 JPanel 的某个子类上。

import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.MouseInputAdapter;

import net.miginfocom.swing.MigLayout;

public class TestScrollPane extends MouseInputAdapter{
    public void mouseEntered(MouseEvent arg0) {System.out.println("Entered " + arg0.getComponent());}
    public void mouseExited(MouseEvent arg0) {System.out.println("Exited " + arg0.getComponent());}
    public void mousePressed(MouseEvent arg0) {System.out.println("Pressed " + arg0.getComponent());}
    public void mouseReleased(MouseEvent arg0) {System.out.println("Released " + arg0.getComponent());}

    public static void main(String[] args){
        JFrame frame = new JFrame();
        frame.setLayout(new MigLayout());
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        TestPane pane1 = new TestPane("Scroll Pane 1");
        TestPane pane2 = new TestPane("Scroll Pane 2");
        frame.add(pane1, "push,grow");
        frame.add(pane2, "push, grow");
        TestMouseListener listener = new TestMouseListener();
        pane1.addMouseListener(listener);
        pane1.addMouseMotionListener(listener);
        pane2.addMouseListener(listener);
        pane2.addMouseMotionListener(listener);
        frame.setVisible(true);
    }
}

class TestPanel2 extends JPanel {
    String name;
    TestPanel2(String name){ 
        this.name = name;
        setLayout(new MigLayout());
        JTextArea area = new JTextArea();
        area.append(name);
        add(area, "push, grow");
    }
    public String toString(){ return name; }
}
class TestPane extends JScrollPane {
    String name;
    TestPane(String name){ 
        this.name = name; 
        TestPanel2 panel = new TestPanel2(name + " panel");
        setViewportView(panel);
    }
    public String toString(){ return name; }
}

在这个例子中,我得到了鼠标进入和退出事件,但我只能通过单击文本区域周围的边框来获取鼠标点击事件。即使我更改了 TestPane 类以将侦听器添加到它的 viewportView 面板,我也无法判断 textArea 中发生了什么。

class TestPane extends JScrollPane {
    String name;
    TestPane(String name){ 
        this.name = name; 
        TestPanel2 panel = new TestPanel2(name + " panel");
        TestMouseListener listener = new TestMouseListener();
        panel.addMouseListener(listener);
        panel.addMouseMotionListener(listener);
        setViewportView(panel);
    }
    public String toString(){ return name; }
}

不过,我无法知道 JPanel 上的内容,因此我无法手动更深入地添加侦听器。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    另一种可能的方法是使用 AWTEventListener,然后冒泡父树以查看您感兴趣的组件是否已被按下或持有已被按下的子组件。例如:

    import java.awt.AWTEvent;
    import java.awt.Component;
    import java.awt.FlowLayout;
    import java.awt.Toolkit;
    import java.awt.event.AWTEventListener;
    import java.awt.event.MouseEvent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.event.MouseInputAdapter;
    
    // import net.miginfocom.swing.MigLayout;
    
    public class TestScrollPane extends MouseInputAdapter {
       public void mouseEntered(MouseEvent arg0) {
          System.out.println("Entered " + arg0.getComponent());
       }
    
       public void mouseExited(MouseEvent arg0) {
          System.out.println("Exited " + arg0.getComponent());
       }
    
       public void mousePressed(MouseEvent arg0) {
          System.out.println("Pressed " + arg0.getComponent());
       }
    
       public void mouseReleased(MouseEvent arg0) {
          System.out.println("Released " + arg0.getComponent());
       }
    
       public static void main(String[] args) {
          JFrame frame = new JFrame();
          // frame.setLayout(new MigLayout());
          frame.getContentPane().setLayout(new FlowLayout());
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          TestPane pane1 = new TestPane("Scroll Pane 1");
          TestPane pane2 = new TestPane("Scroll Pane 2");
          frame.add(pane1, "push,grow");
          frame.add(pane2, "push, grow");
          // !! TestMouseListener listener = new TestMouseListener();
          TestScrollPane listener = new TestScrollPane();
          pane1.addMouseListener(listener);
          pane1.addMouseMotionListener(listener);
          pane2.addMouseListener(listener);
          pane2.addMouseMotionListener(listener);
          frame.pack();
          frame.setVisible(true);
    
          Toolkit.getDefaultToolkit().addAWTEventListener(
                listener.createAWTWindowListener(), AWTEvent.MOUSE_EVENT_MASK);
    
       }
    
       private AWTEventListener createAWTWindowListener() {
          AWTEventListener awt1 = new AWTEventListener() {
    
             @Override
             public void eventDispatched(AWTEvent e) {
                if (MouseEvent.MOUSE_PRESSED == e.getID()) {
                   MouseEvent event = (MouseEvent) e;
                   Component comp = event.getComponent();
    
                   if (comp != null) {
                      String scrollPanelName = recursivelyCheckForScrollPanel(comp);
                      if (scrollPanelName != null) {
                         System.out.println("TestPane pressed. Name: " + scrollPanelName);
                      } else {
                         System.out.println("TestPane not pressed");
                      }
                   }
                }
             }
    
             private String recursivelyCheckForScrollPanel(Component comp) {
                if (comp instanceof TestPane) {
                   return comp.toString();
                } else {
                   comp = comp.getParent();
                   if (comp != null) {
                      return recursivelyCheckForScrollPanel(comp);
                   }
                }
                return null;
             }
          };
          return awt1;
       }
    }
    
    class TestPanel2 extends JPanel {
       String name;
    
       TestPanel2(String name) {
          this.name = name;
          // setLayout(new MigLayout());
          JTextArea area = new JTextArea(5, 20);
          area.append(name);
          add(area, "push, grow");
       }
    
       public String toString() {
          return name;
       }
    }
    
    class TestPane extends JScrollPane {
       String name;
    
       TestPane(String name) {
          this.name = name;
          TestPanel2 panel = new TestPanel2(name + " panel");
          setViewportView(panel);
       }
    
       public String toString() {
          return name;
       }
    }
    

    注意:请参阅 this question 以及 StanislovL 和 mkorbel 的答案以了解更多信息。

    【讨论】:

    • @mKorbel:是的,它应该看起来很熟悉!并感谢您的代码!
    • 不,干得好,因为 JScrollPane (JViewport) 不是 JComponent,也不是容器,使用 getDeepestComponentAt 不能正常工作,必须有另一种方法 :-)
    • 你能在这里看看我删除的帖子吗
    • 我对这种方法持乐观态度,尽管我在细节上遇到了一些障碍。我实际上希望为每个滚动窗格设置不同的侦听器,但 Toolkit.getDefaultToolkit().addAWTEventListener 似乎不允许这种歧视。
    • 搞定了。实际上简化了我的代码,一旦我意识到我也有太多的听众让事情变得过于复杂。谢谢,@HovercraftFullOfEels!
    【解决方案2】:

    您应该将鼠标侦听器添加到 JScrollPane 的视图而不是滚动窗格本身,因为滚动窗格仅由角和滚动条组成。

    yourJScrollPane.getViewport().getView().addMouseListener(yourMouseListener);
    

    此代码段会将您的鼠标侦听器添加到 JScrollPane 的一个视口组件。

    【讨论】:

      【解决方案3】:

      您将遇到的问题是,如果面板包含的内容也有鼠标侦听器,您将永远不会收到这些事件的通知(鼠标事件往往会被链中较高的那些事件消耗)

      您可以使用 addNotify,类似于 JTable 的工作方式。然后,您将遍历父链,直到获得所需的组件。

      这当然是假设您并没有打算使用鼠标监听器...

      【讨论】:

        猜你喜欢
        • 2012-03-11
        • 1970-01-01
        • 2012-04-05
        • 2013-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-02
        相关资源
        最近更新 更多