0

easy question:

I draw more than one Rectangel2D (or other shapes) in one JPane with overlapping. I want to select the one on the top by click the overlapping area and do something, e.g. change color or drag.

But I can't select the one which I want.

The order of the Shapes is not under control.

I find a method setComponentZOrder, but that is for component not for Rectangel2D.

I think this is a typical question, but I can't find the answer.

Excessstone
  • 61
  • 1
  • 6
  • Welcome to Stack Overflow, please take the [tour] and go through the [help], then learn [ask] and once you've done that, come back with a proper [mcve] or [Short, Self Contained, Correct Example](http://sscce.org/) that demonstrates your issue. – Frakcool Feb 02 '17 at 17:32

1 Answers1

4

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;
            }
        }
    }
    
    // ....
    
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373