javaswingdrag-and-dropdragmousemotionlistener

dragging disables other MouseMotionListener


I have two components each which its own MouseMotionListener. When I move the mouse from the first to the second Component while dragging the first Component, the MouseMotionListener seems to be disabled for the second Component, i.e. mouseMoved is not called at all although I move the mouse over the second component. How do I avoid that "disabling"?

Example:

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class DragTest implements MouseMotionListener {

    private static JPanel p1 = new JPanel();
    private static JPanel p2 = new JPanel();

    public DragTest() {

    }

    public static void main(String[] args) {
        p1.setBackground(Color.RED);
        p1.addMouseMotionListener(new DragTest());
        p2.setBackground(Color.BLUE);
        p2.addMouseMotionListener(new DragTest());

        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(100, 100, 600, 300);
        frame.setLayout(new GridLayout(1, 2));

        frame.add(p1);
        frame.add(p2);

        frame.setVisible(true);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        if (e.getSource() == p1) {
            System.out.println("mouse movement in p1");
        } else if (e.getSource() == p2) {
            System.out.println("mouse movement in p2");
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (e.getSource() == p1) {
            System.out.println("mouse drag in p1");
        } else if (e.getSource() == p2) {
            System.out.println("mouse drag in p2");
        }
    }
}

Solution

  • Disclaimer: This is a VERY, VERY basic example of the core drag'n'drop API and is based on this example and this example and this example and designed to simply demonstrate the possibility to ascertain the location of a prescribed drop

    So when it comes to dragging things around, especially across components, it's generally better to use the drag'n'drop API over simply using MouseMotionListener and MouseListener. This is what the API has been designed for and provides notifications to both targets about the nature of the operation

    The following example simply stores the location's at which you drop something and paints a nice little dot there

    DragNDrop

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.GridLayout;
    import java.awt.Point;
    import java.awt.datatransfer.DataFlavor;
    import java.awt.datatransfer.Transferable;
    import java.awt.datatransfer.UnsupportedFlavorException;
    import java.awt.dnd.DnDConstants;
    import java.awt.dnd.DragGestureEvent;
    import java.awt.dnd.DragGestureListener;
    import java.awt.dnd.DragSource;
    import java.awt.dnd.DragSourceDragEvent;
    import java.awt.dnd.DragSourceDropEvent;
    import java.awt.dnd.DragSourceEvent;
    import java.awt.dnd.DragSourceListener;
    import java.awt.dnd.DropTarget;
    import java.awt.dnd.DropTargetDragEvent;
    import java.awt.dnd.DropTargetDropEvent;
    import java.awt.dnd.DropTargetEvent;
    import java.awt.dnd.DropTargetListener;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.LineBorder;
    import javax.swing.border.MatteBorder;
    
    public class DragAndDropTest {
    
        public static void main(String[] args) {
            new DragAndDropTest();
        }
    
        public DragAndDropTest() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane() {
                setLayout(new GridLayout(1, 2));
                add(new DropPane());
                add(new DragPane());
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.dispose();
            }
    
        }
    
        public class DragPane extends JPanel {
    
            private DragSource ds;
            private Transferable transferable;
    
            public DragPane() {
                ds = new DragSource();
                transferable = new Transferable() {
    
                    @Override
                    public DataFlavor[] getTransferDataFlavors() {
                        return new DataFlavor[]{DataFlavor.stringFlavor};
                    }
    
                    @Override
                    public boolean isDataFlavorSupported(DataFlavor flavor) {
                        return DataFlavor.stringFlavor.equals(flavor);
                    }
    
                    @Override
                    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
                        return "This is a test";
                    }
                };
                ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() {
                    @Override
                    public void dragGestureRecognized(DragGestureEvent dge) {                   
                        // This is where you would export the data you want
                        // to transfer
                        ds.startDrag(dge, DragSource.DefaultCopyDrop, transferable, new DragSourceListener() {
    
                            @Override
                            public void dragEnter(DragSourceDragEvent dsde) {
                            }
    
                            @Override
                            public void dragOver(DragSourceDragEvent dsde) {
                            }
    
                            @Override
                            public void dropActionChanged(DragSourceDragEvent dsde) {
                            }
    
                            @Override
                            public void dragExit(DragSourceEvent dse) {
                            }
    
                            @Override
                            public void dragDropEnd(DragSourceDropEvent dsde) {
                            }
    
                        });
                    }
                });
    
                setLayout(new GridBagLayout());
                add(new JLabel("Drag from here"));
                setBorder(new LineBorder(Color.RED));
            }
    
        }
    
        public class DropPane extends JPanel {
    
            private List<Point> dropPoints;
    
            public DropPane() {
                dropPoints = new ArrayList<>(25);
                setDropTarget(new DropTarget(this, new DropTargetListener() {
    
                    @Override
                    public void dragEnter(DropTargetDragEvent dtde) {
                    }
    
                    @Override
                    public void dragOver(DropTargetDragEvent dtde) {
                    }
    
                    @Override
                    public void dropActionChanged(DropTargetDragEvent dtde) {
                    }
    
                    @Override
                    public void dragExit(DropTargetEvent dte) {
                    }
    
                    @Override
                    public void drop(DropTargetDropEvent dtde) {
                        // Normally here, I'd inspect the Transferable and make sure
                        // what is been dropped and can be imported, I'd then go through
                        // the process of unwrapping the data from the Transferable and 
                        // processing it appropriatly, but in this example, I really don't
                        // care, I just care about WHERE the event occured
                        dropPoints.add(dtde.getLocation());
                        dtde.dropComplete(true);
                        repaint();
                    }
                }));
                setLayout(new GridBagLayout());
                add(new JLabel("Drop to here"));
                setBorder(new MatteBorder(1, 1, 1, 0, Color.RED));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(Color.RED);
                for (Point p : dropPoints) {
                    g.fillOval(p.x - 2, p.y - 2, 5, 5);
                }
            }
    
        }
    
    }