MouseMotionListener en Java Swing, usándolo con componentes dentro de componentes, etc.

Estoy trabajando en una interfaz de usuario táctil en Swing. Si bien sé que esto no es óptimo, tengo un plazo breve y no tengo tiempo para los paquetes de GUI específicos de la pantalla táctil (si los hay).

Quiero que mis usuarios puedan ‘deslizar’ su dedo por la pantalla, y la vista de un JScrollPane especial que hice se mueve con él. El código es muy simple –

public class PanScrollPane extends JScrollPane implements MouseMotionListener{ public PanScrollPane() { super(); this.addMouseMotionListener(this); } @Override public void mouseDragged(MouseEvent arg0) { System.out.println("Mouse Dragged!"); } @Override public void mouseMoved(MouseEvent arg0) { System.out.println("Mouse Moved!"); } 

El problema que tengo es que JScrollPane es un contenedor para todo tipo de JComponents. Cuando comencé a trabajar en esto, me imaginé que MouseMovedEvent y MouseDraggedEvent se propagarían por el ‘árbol GUI’, hasta que encontraran un Componente con un oyente específicamente para ese evento. Ahora parece que cualquier componente que agregue al panScrollPane bloquea cualquiera de estos eventos de MouseMotion, dejándome incapacitado.

  panScrollPane.add(new JButton("This thing blocks any mouse motion events")); 

Pensé que la propagación del evento MouseEvent a mano (agregar oyentes a cada componente y hacer que envíen el evento a su padre) funcionaría. Sin embargo, esta es una tarea que exige mucho tiempo y, como preferiría dedicarme a trabajar en otras cuestiones, me preguntaba si alguno de ustedes sabe cómo solucionar este problema.

Gracias por leer y espero gracias por responder! 🙂

editar: Para hacer mis intenciones más claras. Solo quiero que los eventos de mousemotion sean capturados por panPanel, cualquier otro evento (como MouseClick, MouseRelease) debería procesarse normalmente

¿Qué hay de usar un GlassPane ? Creo que tiene la intención de abordar exactamente este tipo de situaciones.

Este enfoque ad hoc aprovecha las acciones JScrollPane existentes que se utilizan generalmente en enlaces de teclas . Deberá sintonizar N en su implementación de Scrollable .

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.Action; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.Timer; /** @see http://stackoverflow.com/questions/7201509 */ public class ScrollAction extends JPanel { private static final int TILE = 64; private static final int DELTA = 16; public ScrollAction() { this.setOpaque(false); this.setFocusable(true); this.setPreferredSize(new Dimension(50 * TILE, 50 * TILE)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.lightGray); int w = this.getWidth() / TILE + 1; int h = this.getHeight() / TILE + 1; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { if ((row + col) % 2 == 0) { g.fillRect(col * TILE, row * TILE, TILE, TILE); } } } } private void display() { JFrame f = new JFrame("ScrollAction"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JScrollPane scrollPane = new JScrollPane(this); final ScrollTimer left = new ScrollTimer(scrollPane, "scrollLeft"); final ScrollTimer right = new ScrollTimer(scrollPane, "scrollRight"); final ScrollTimer up = new ScrollTimer(scrollPane, "scrollUp"); final ScrollTimer down = new ScrollTimer(scrollPane, "scrollDown"); final JViewport viewPort = scrollPane.getViewport(); viewPort.setPreferredSize(new Dimension(5 * TILE, 5 * TILE)); viewPort.addMouseMotionListener(new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { left.stop(); if (e.getX() < DELTA) { left.start(); } right.stop(); if (e.getX() > viewPort.getWidth() - DELTA) { right.start(); } up.stop(); if (e.getY() < DELTA) { up.start(); } down.stop(); if (e.getY() > viewPort.getHeight() - DELTA) { down.start(); } } }); f.add(scrollPane); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } private static final class ScrollTimer implements ActionListener { private static int N = 10; private static int DELAY = 100; private String cmd; private Timer timer; private Action action; private JScrollPane scrollPane; private int count; public ScrollTimer(JScrollPane scrollPane, String action) { this.cmd = action; this.timer = new Timer(DELAY, this); this.action = scrollPane.getActionMap().get(action); this.scrollPane = scrollPane; } @Override public void actionPerformed(ActionEvent e) { if (count++ < N) { action.actionPerformed(new ActionEvent(scrollPane, 0, cmd)); } else { timer.stop(); } } public void start() { count = 0; timer.start(); } public void stop() { timer.stop(); count = 0; } } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new ScrollAction().display(); } }); } } 

Obtener mouseEvents para un componente y todos sus hijos es … complicado de hacer bien. Puede considerar confiar en un código estable (y extensamente probado :-). La forma jdk7 de hacerlo es usar un JLayer (que registra internamente un AWTEventListener ya que tiene todos los privilegios). Para versiones anteriores, puede usar su predecesor JXLayer