One of the side effects of a drag operation is the fact that mouseClicked
won't be called. Why is this important? Basically you can use this fact to make a decision about whether a object should be deselected or not within the mouseClicked
event instead of something like mousePressed
or mouseReleased
.
It does require you to maintain some information about the current and previous states, so you know whether the object was just selected or was previously selected, but the basic idea works well.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class WhatADrag {
public static void main(String[] args) {
new WhatADrag();
}
public WhatADrag() {
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 {
private List<Rectangle> boxes;
private Rectangle selected;
public TestPane() {
boxes = new ArrayList<>(25);
int x = 0;
int y = 0;
for (int index = 0; index < 10; index++) {
boxes.add(new Rectangle(x, y, 100, 100));
x += 25;
y += 25;
}
MouseAdapter ma = new MouseAdapter() {
private Rectangle previous;
private Point delta;
@Override
public void mousePressed(MouseEvent e) {
List<Rectangle> reversed = new ArrayList<>(boxes);
Collections.reverse(reversed);
previous = selected;
if (selected == null || !selected.contains(e.getPoint())) {
for (Rectangle box : reversed) {
if (box.contains(e.getPoint())) {
selected = box;
delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
repaint();
break;
}
}
if (selected != null) {
boxes.remove(selected);
boxes.add(boxes.size() - 1, selected);
}
} else if (selected != null) {
delta = new Point(e.getX() - selected.x, e.getY() - selected.y);
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (selected == previous && selected != null && selected.contains(e.getPoint())) {
selected = null;
repaint();
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (selected != null) {
int x = e.getX() - delta.x;
int y = e.getY() - delta.y;
selected.x = x;
selected.y = y;
repaint();
}
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Rectangle box : boxes) {
if (box != selected) {
g2d.setColor(Color.BLUE);
g2d.fill(box);
g2d.setColor(Color.BLACK);
g2d.draw(box);
}
}
if (selected != null) {
g2d.setColor(Color.CYAN);
g2d.fill(selected);
g2d.setColor(Color.BLUE);
g2d.draw(selected);
}
g2d.dispose();
}
}
}
I am just so lost. I am looking at all this code and don't even understand what it is doing.
Yeah, I feel like this about my code a lot.
Basically...
First: mousePressed
is called, I reverse my list of objects, because the top most component will be the last in the list (this is my requirement), I store the currently selected object in the previous
variable, I use this to determine if there has been a change in selection or not. I check to see if the user clicked the selected
object or not, if they did, we can basically skip every thing else. If not, we determine what they clicked, if anything. The delta
is simply the difference between the place they clicked and the location of the object, this is used to make the drag more natural
If no drag occurs: mouseClicked
is called. We test to see if the selected
object is equal to the previous
object and if the mouse was clicked within the selected
object, if these are true
, then the currently selected
object should be deselected. Otherwise the user has basically just changed the selection, so we don't want to immediately deselect it.
Else if a drag occurs: mouseDragged
is called. We simply check to see if something is selected
, we calculate the difference between the current mouse position and the "click offset" and update the position of the selected
object
Clear as mud :P
One thing to also remember is mouseReleased
will always be called after mousePressed
, even if mouseClicked
isn't (which is called after mouseReleased
when no dragging occurs).