Search code examples
javadrag-and-dropmouseeventmouselistener

Java Drag and Drop and Mouse Listeners


I am using logic like the following with a touch interface, so I'd like to be able to use mousePressed to initiate a drag, yet at the same time be able to use mouseClicked or mouseReleased if the user does not drag.

On mousePressed, I have a drag being started. On mouseClicked, I want it to print out "mouseClicked", and to print mouseReleased on "mouseReleased" but for some reason the

    dropLabel.getTransferHandler().exportAsDrag(dropLabel, e,
            TransferHandler.COPY);

swallows the event causing mouseClicked and mouseReleased events not be fired even when I don't move the mouse. Notice that commenting out that line causes all listeners to be fired (but obviously the drag isn't started).

How can I get all mouse events to happen the way I want, specifically mouseClicked/mouseReleased when the user doesn't do any dragging, while still being able to exportAsDragged if the user does drag? Any hacks/cracks/reflection solutions are welcome.

Here is some sample code I am working with that illustrates my actual problem:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.TransferHandler;

public class DragTest extends JFrame implements MouseMotionListener,
        MouseListener {

    private JPanel leftPanel = new JPanel(null);
    private JPanel rightPanel = new JPanel(null);
    JLabel dropLabel;

    public DragTest() {
        this.setLayout(new GridLayout(1, 2));

        leftPanel.setBorder(BorderFactory.createLineBorder(Color.black));
        rightPanel.setBorder(BorderFactory.createLineBorder(Color.black));
        this.add(leftPanel);
        this.add(rightPanel);
        leftPanel.addMouseListener(this);
        leftPanel.addMouseMotionListener(this);

        JTextArea area = new JTextArea();

        rightPanel.setLayout(new GridLayout(1, 1));
        rightPanel.add(area);

        dropLabel = new JLabel("drop");
        dropLabel.setTransferHandler(new TransferHandler("text"));

    }

    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("mousePressed");


        Dimension labelSize = dropLabel.getPreferredSize();
        dropLabel.setSize(labelSize);
        int x = e.getX() - labelSize.width / 2;
        int y = e.getY() - labelSize.height / 2;
        dropLabel.setLocation(x, y);
        leftPanel.add(dropLabel);


        dropLabel.getTransferHandler().exportAsDrag(dropLabel, e,
                TransferHandler.COPY);

        repaint();

    }

    @Override
    public void mouseDragged(MouseEvent me) {
        System.out.println("mouseDragged");
    }

    @Override
    public void mouseMoved(MouseEvent e) {

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("mouseClicked");
    }

    @Override
    public void mouseReleased(MouseEvent e) {

        System.out.println("mouseReleased");

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    public static void main(String[] args) {

        DragTest frame = new DragTest();
        frame.setVisible(true);
        frame.setSize(600, 400);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Solution

  • Do you really need the mouseReleased to fire or what? I think this should work well:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    
    import javax.swing.BorderFactory;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.TransferHandler;
    
    public class DragTest extends JFrame implements MouseMotionListener,
            MouseListener {
    
        private JPanel leftPanel = new JPanel(null);
        private JPanel rightPanel = new JPanel(null);
        JLabel dropLabel;
    
        public DragTest() {
            this.setLayout(new GridLayout(1, 2));
    
            leftPanel.setBorder(BorderFactory.createLineBorder(Color.black));
            rightPanel.setBorder(BorderFactory.createLineBorder(Color.black));
            this.add(leftPanel);
            this.add(rightPanel);
            leftPanel.addMouseListener(this);
            leftPanel.addMouseMotionListener(this);
    
            JTextArea area = new JTextArea();
    
            rightPanel.setLayout(new GridLayout(1, 1));
            rightPanel.add(area);
    
            dropLabel = new JLabel("drop");
            dropLabel.setTransferHandler(new TransferHandler("text"));
    
        }
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println("mousePressed");
            Dimension labelSize = dropLabel.getPreferredSize();
            dropLabel.setSize(labelSize);
            int x = e.getX() - labelSize.width / 2;
            int y = e.getY() - labelSize.height / 2;
            dropLabel.setLocation(x, y);
            leftPanel.add(dropLabel);
    
    
    
            repaint();
    
        }
    
        @Override
        public void mouseDragged(MouseEvent me) {
            System.out.println("mouseDragged");
                dropLabel.getTransferHandler().exportAsDrag(dropLabel, me,
                        TransferHandler.COPY);
        }
    
        @Override
        public void mouseMoved(MouseEvent e) {
        }
    
        @Override
        public void mouseClicked(MouseEvent e) {
            System.out.println("mouseClicked");
        }
    
        @Override
        public void mouseReleased(MouseEvent e) {
            System.out.println("mouseReleased");
    
        }
    
        @Override
        public void mouseEntered(MouseEvent e) {
    
        }
    
        @Override
        public void mouseExited(MouseEvent e) {
    
        }
    
        public static void main(String[] args) {
    
            DragTest frame = new DragTest();
            frame.setVisible(true);
            frame.setSize(600, 400);
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    }
    

    I just moved the call to start the drag event into the mouseDragged call, this way if you just click your mouse then everything calls normally. However, if you drag the mouse, it will fire the drag and drop call, which still does block other calls, but again only if you drag. If you just click, it all fires normally.