The order that a Shape such as a Rectangle2D are drawn is determined by the order that they are held in whatever collection holds them as this collection is iterated through in your JComponent's (such as a JPanel's) painting method. Thus the last items in the collection, be it an array or ArrayList or LinkedList, is drawn on top.
So if you want to get the top Shape on mouse press or click, then the key will be to iterate through the same collection backwards, getting the first Shape that contains(Point p)
the mouse Point location. That's it.
For example, please look at my code here which does just his in this method which is called from the MouseListener (actually MouseAdapater):
public Shape getShapeAtPoint(Point p) {
Shape shapeAtPoint = null;
for (int i = shapeList.size() - 1; i >= 0; i--) {
if (shapeList.get(i).contains(p)) {
return shapeList.get(i);
}
}
return shapeAtPoint;
}
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
@SuppressWarnings("serial")
public class RandomShapes extends JPanel {
private static final Stroke STROKE = new BasicStroke(4f);
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final int SHAPE_COUNT = 30;
private static final int SHAPE_WIDTH = 100;
private static final int SHAPE_HEIGHT = SHAPE_WIDTH;
private List<Path2D> paths = new ArrayList<>();
private Map<Path2D, Color> colorMap = new HashMap<>();
private Random random = new Random();
public RandomShapes() {
for (int i = 0; i < SHAPE_COUNT; i++) {
Shape shape = createRandomShape(i);
Path2D path = new Path2D.Double(shape);
paths.add(path);
colorMap.put(path, createRandomColor());
}
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
private class MyMouse extends MouseAdapter {
private Path2D selectedPath = null;
private Point p1;
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = paths.size() - 1; i >= 0; i--) {
Path2D path = paths.get(i);
if (path.contains(e.getPoint())) {
selectedPath = path;
p1 = e.getPoint();
paths.remove(selectedPath);
paths.add(selectedPath);
repaint();
break;
}
}
}
private void movePath(MouseEvent e) {
Point p2 = e.getPoint();
int tx = p2.x - p1.x;
int ty = p2.y - p1.y;
p1 = p2;
AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
selectedPath.transform(at);
repaint();
}
@Override
public void mouseDragged(MouseEvent e) {
if (selectedPath != null) {
movePath(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (selectedPath != null) {
movePath(e);
}
selectedPath = null;
}
}
private Color createRandomColor() {
float min = 0.2f;
float h = random.nextFloat();
float s = min * random.nextFloat() + (1f - min);
float b = min * random.nextFloat() + (1f - min);
return Color.getHSBColor(h, s, b);
}
private Shape createRandomShape(int i) {
Dimension size = getPreferredSize();
int x = random.nextInt(size.width - SHAPE_WIDTH);
int y = random.nextInt(size.height - SHAPE_HEIGHT);
switch (i % 3) {
case 0:
return new Ellipse2D.Double(x, y, SHAPE_WIDTH, SHAPE_HEIGHT);
case 1:
return new Rectangle2D.Double(x, y, SHAPE_WIDTH, SHAPE_HEIGHT);
case 2:
return new RoundRectangle2D.Double(x, y, SHAPE_WIDTH, SHAPE_HEIGHT, 15, 15);
default:
break;
}
return null;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(STROKE);
for (Path2D path : paths) {
g2.setColor(colorMap.get(path));
g2.fill(path);
g2.setColor(colorMap.get(path).darker());
g2.draw(path);
}
}
@Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("RandomShapes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RandomShapes());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Note that if you want to delete a shape on right mouse click (MouseEvent.BUTTON3), this edit to the MyMouse MouseAdapter class can make this happen (see comments in code):
private class MyMouse extends MouseAdapter {
private Path2D selectedPath = null;
private Point p1;
@Override
public void mousePressed(MouseEvent e) {
int button = e.getButton();
// if the *middle* mouse button is pushed, ignore it
if (button == MouseEvent.BUTTON2) {
return;
}
// iterate through all the shapes,
// starting at the top of the list (last drawn shapes)
for (int i = paths.size() - 1; i >= 0; i--) {
Path2D path = paths.get(i);
if (path.contains(e.getPoint())) {
// if the mouse presses on one of the shapes
selectedPath = path;
p1 = e.getPoint();
// remove the shape from the collection
paths.remove(selectedPath);
if (button == MouseEvent.BUTTON1) {
// re-add it back in at the end of the collection
// only if the left mouse button has been pressed
paths.add(selectedPath);
}
repaint();
break;
}
}
}
// ....
}