Java swing如何对自定义绘画进行分层

Java swing如何对自定义绘画进行分层,java,swing,layer,Java,Swing,Layer,背景 我目前正在从事一个项目,我希望能够在JPanel上画一个三角形、圆形和矩形,并将它们四处移动 当一个图形被移动时,它应该结束在“顶部”,以便它覆盖处于重叠位置的其他图形,当一个图形被移动时,选择“顶部”;如果多个图形覆盖鼠标指针的位置,则选择顶部的一个 我的问题 如果我将形状拖到同一个位置,我无法解决如何解决形状不会彼此重叠的问题。它们只是停留在同一层,如果我把它们都叠在一起,我仍然会首先选择矩形 如果我将形状拖到同一个位置,我无法解决如何解决形状不会彼此重叠的问题 面板上的组件是根据组件

背景

我目前正在从事一个项目,我希望能够在
JPanel
上画一个三角形、圆形和矩形,并将它们四处移动

当一个图形被移动时,它应该结束在“顶部”,以便它覆盖处于重叠位置的其他图形,当一个图形被移动时,选择“顶部”;如果多个图形覆盖鼠标指针的位置,则选择顶部的一个

我的问题

如果我将形状拖到同一个位置,我无法解决如何解决形状不会彼此重叠的问题。它们只是停留在同一层,如果我把它们都叠在一起,我仍然会首先选择矩形

如果我将形状拖到同一个位置,我无法解决如何解决形状不会彼此重叠的问题

面板上的组件是根据组件
ZOrder
绘制的。具有最低ZOrder的组件最后绘制

因此,在
mousespressed
method中,当您选择要拖动的组件时,可以更改其顺序:

Component child = e.getComponent();
child.getParent().setComponentZOrder(child, 0);
编辑:

我以前没有注意到那个代码

切勿直接调用paintComponent()。

Swing具有父/子关系。只需将3个形状面板中的每一个添加到父面板。然后,父面板将基于上面描述的ZOrder绘制子组件。去掉这三行代码

他们只是停留在同一层

这是因为您绘制它们的顺序:

drawRec.paintComponent(g);
drawCirc.paintComponent(g);
drawTri.paintComponent(g);
这将导致
drawTri
始终绘制在其他图形的顶部(因为您总是最后绘制)。以类似的方式,
drawRec
将被绘制在其他图形的底部(因为您总是首先绘制它)。然后<代码> DracCurc < /代码>将被画在中间。这里没有绘画的动态顺序(让我说出来)。不管你拖动或不拖动什么,它们总是按照那个顺序绘制的

一个解决方案可能是将它们放在某种列表或数组中,当拖动一个形状时,将其放在列表的最后,并将其他形状移到它之前。如果您将其与从列表中依次绘制所有形状相结合,那么您将获得所需的结果


…如果我把它们都叠在一起,我仍然会先拾取矩形

这是因为
ClickListener
类中的
mousePressed
是如何工作的:它首先检查矩形是否被单击,然后检查其他矩形。这意味着,如果矩形与另一个形状重叠,则该矩形将始终具有优先级

一种解决方案可能还是将所有形状放在一个数据结构中,您可以在其中修改它们的选择顺序。例如,一个列表或数组,假设越靠近顶部是一个形状,那么它将越晚出现在列表中。然后,当用户单击某个位置时,您将检查从列表中最后一个到第一个的形状。如果你发现了什么,你会立即打破循环,选择你发现的。如果没有找到任何形状,则用户单击了当前没有形状的面板


我几乎可以肯定,对于这个问题,必须有比列表或数组更有效的数据结构(因为你必须在线性时间内迭代所有形状以找到单击的形状),但我不是碰撞检测方面的专家,因此,为了保持简单,我将坚持使用它。但我们还需要做另一个操作:更改绘制和单击形状的选择顺序。长话短说,我们可以使用
LinkedHashSet
进行作业,因为:

  • 它保持形状的顺序
  • 我们可以快速有效地更改顺序,首先将单击的形状从其当前位置(固定时间)移除,然后将其添加回
    LinkedHashSet
    (也是固定时间),基本上将其放在插入顺序的最后。这就意味着我们必须使用集合中的最后一个元素作为最上面的元素。这很好,因为当绘制时,我们可以按照在集合中找到的顺序对集合中的所有形状进行迭代,所以最后一个形状将绘制在最后(这意味着在所有其他形状之上)。这同样表示单击时选择形状:我们迭代所有元素,然后选择最后一个包含用户单击点的元素。如果
    LinkedHashSet
    有一个降序迭代器(按插入顺序),那么我们也可以在给定的点击点对形状的搜索进行一点优化,但它没有优化(至少在Java 8中,下面的演示/示例代码适用),所以我将从一开始就坚持迭代,每次检查所有形状,并保留最后找到的包含单击点的形状
  • 最后,我建议您对形状使用API类
    java.awt.Shape
    ,因为这使您能够创建任意形状,并获得
    contains
    方法来检查其中是否存在点、绘图/填充功能、边界、路径迭代器等等

    总结以上内容,并给出示例代码:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Paint;
    import java.awt.Point;
    import java.awt.Shape;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.util.Collection;
    import java.util.LinkedHashSet;
    import java.util.Objects;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class NonComponentMain {
        
        public static Shape createRectangle(final double width,
                                            final double height) {
            return new Rectangle2D.Double(0, 0, width, height);
        }
        
        public static Shape createEllipse(final double width,
                                          final double height) {
            return new Ellipse2D.Double(0, 0, width, height);
        }
        
        public static Shape createCircle(final double radius) {
            return createEllipse(radius + radius, radius + radius);
        }
        
        public static class MoveableShapeHolder {
            private final Shape shape;
            private final Paint paint;
            private final Rectangle2D originalBounds;
            private double offsetX, offsetY;
            
            public MoveableShapeHolder(final Shape shape,
                                       final Paint paint) {
                this.shape = Objects.requireNonNull(shape);
                this.paint = paint;
                offsetX = offsetY = 0;
                originalBounds = shape.getBounds2D();
            }
            
            public void paint(final Graphics2D g2d) {
                final AffineTransform originalAffineTransform = g2d.getTransform();
                final Paint originalPaint = g2d.getPaint();
                g2d.translate(offsetX, offsetY);
                if (paint != null)
                    g2d.setPaint(paint);
                g2d.fill(shape);
                g2d.setPaint(originalPaint);
                g2d.setTransform(originalAffineTransform);
            }
            
            public void moveTo(final double newBoundsCenterX,
                               final double newBoundsCenterY) {
                offsetX = newBoundsCenterX - originalBounds.getCenterX();
                offsetY = newBoundsCenterY - originalBounds.getCenterY();
            }
            
            public void moveBy(final double dx,
                               final double dy) {
                offsetX += dx;
                offsetY += dy;
            }
            
            public boolean contains(final Point2D pt) {
                return shape.contains(pt.getX() - offsetX, pt.getY() - offsetY);
            }
            
            public Point2D getTopLeft() {
                return new Point2D.Double(offsetX + originalBounds.getX(), offsetY + originalBounds.getY());
            }
            
            public Point2D getCenter() {
                return new Point2D.Double(offsetX + originalBounds.getCenterX(), offsetY + originalBounds.getCenterY()); //Like 'getTopLeft' but with adding half the size.
            }
            
            public Point2D getBottomRight() {
                return new Point2D.Double(offsetX + originalBounds.getMaxX(), offsetY + originalBounds.getMaxY()); //Like 'getTopLeft' but with adding the size of the bounds.
            }
        }
        
        public static class DrawPanel extends JPanel {
            
            private class MouseDrag extends MouseAdapter {
                private MoveableShapeHolder current;
                private Point origin;
                private Point2D center;
                
                @Override
                public void mousePressed(final MouseEvent e) {
                    current = null;
                    center = null;
                    final Point evtLoc = e.getPoint();
                    for (final MoveableShapeHolder moveable: moveables)
                        if (moveable.contains(evtLoc))
                            current = moveable; //Keep the last moveable found to contain the click point! It's important to be the last one, because the later the moveable appears in the collection, the closer to top its layer.
    
                    if (current != null) { //If a shape was clicked...
                        
                        //Initialize MouseDrag's state:
                        origin = e.getPoint();
                        center = current.getCenter();
                        
                        //Move to topmost layer:
                        moveables.remove(current); //Remove from its current position.
                        moveables.add(current); //Move to last (topmost layer).
                        
                        //Rapaint panel:
                        repaint();
                    }
                }
                
                @Override
                public void mouseDragged(final MouseEvent e) {
                    if (current != null) { //If we are dragging something (and not empty space), then:
                        current.moveTo(center.getX() + e.getX() - origin.x, center.getY() + e.getY() - origin.y);
                        repaint();
                    }
                }
    
                @Override
                public void mouseReleased(final MouseEvent e) {
                    current = null;
                    origin = null;
                    center = null;
                }
            }
            
            private final LinkedHashSet<MoveableShapeHolder> moveables;
            
            public DrawPanel() {
                moveables = new LinkedHashSet<>();
                final MouseAdapter ma = new MouseDrag();
                super.addMouseMotionListener(ma);
                super.addMouseListener(ma);
            }
            
            /**
             * Warning: all operations on the returned value must be made on the EDT.
             * @return 
             */
            public Collection<MoveableShapeHolder> getMoveables() {
                return moveables;
            }
            
            @Override
            protected void paintComponent(final Graphics g) {
                super.paintComponent(g);
                moveables.forEach(moveable -> moveable.paint((Graphics2D) g)); //Topmost moveable is painted last.
            }
            
            @Override
            public Dimension getPreferredSize() {
                if (isPreferredSizeSet())
                    return super.getPreferredSize();
                final Dimension preferredSize = new Dimension();
                moveables.forEach(moveable -> {
                    final Point2D max = moveable.getBottomRight();
                    preferredSize.width = Math.max(preferredSize.width, (int) Math.ceil(max.getX()));
                    preferredSize.height = Math.max(preferredSize.height, (int) Math.ceil(max.getY()));
                });
                return preferredSize;
            }
        }
        
        private static void createAndShowGUI() {
            final DrawPanel drawPanel = new DrawPanel();
            final Collection<MoveableShapeHolder> moveables = drawPanel.getMoveables();
            
            MoveableShapeHolder moveable = new MoveableShapeHolder(createRectangle(100, 50), Color.RED);
            moveable.moveTo(100, 75);
            moveables.add(moveable);
            
            moveable = new MoveableShapeHolder(createCircle(40), Color.GREEN);
            moveable.moveTo(125, 100);
            moveables.add(moveable);
            
            moveable = new MoveableShapeHolder(createRectangle(25, 75), Color.BLUE);
            moveable.moveTo(125, 75);
            moveables.add(moveable);
            
            final JFrame frame = new JFrame("Click to drag");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(drawPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
        
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(NonComponentMain::createAndShowGUI);
        }
    }
    
    导入java.awt.Color;
    导入java.awt.Dimension;
    导入java.awt.Graphics;
    导入java.awt.Graphics2D;
    导入java.awt.Paint;
    导入java.awt.Point;
    导入java.awt.Shape;
    导入java.awt.event.MouseAdapter;
    导入java.awt.event.MouseEvent;
    导入java.awt.geom.AffineTransform;
    导入java.awt.geom.Ellipse2D;
    导入java.awt.geom.Point2D;
    导入java.awt.geom.Rectangle2D;
    导入java.util.Collection;
    导入java.util.LinkedHashSet;
    导入java.util.Objects;
    导入javax.swing.JFrame;
    导入javax.swing.JPanel;
    导入javax.swing.SwingUtilities;
    公共类非组件主{
    P
    
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Paint;
    import java.awt.Point;
    import java.awt.Shape;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.util.Collection;
    import java.util.LinkedHashSet;
    import java.util.Objects;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class NonComponentMain {
        
        public static Shape createRectangle(final double width,
                                            final double height) {
            return new Rectangle2D.Double(0, 0, width, height);
        }
        
        public static Shape createEllipse(final double width,
                                          final double height) {
            return new Ellipse2D.Double(0, 0, width, height);
        }
        
        public static Shape createCircle(final double radius) {
            return createEllipse(radius + radius, radius + radius);
        }
        
        public static class MoveableShapeHolder {
            private final Shape shape;
            private final Paint paint;
            private final Rectangle2D originalBounds;
            private double offsetX, offsetY;
            
            public MoveableShapeHolder(final Shape shape,
                                       final Paint paint) {
                this.shape = Objects.requireNonNull(shape);
                this.paint = paint;
                offsetX = offsetY = 0;
                originalBounds = shape.getBounds2D();
            }
            
            public void paint(final Graphics2D g2d) {
                final AffineTransform originalAffineTransform = g2d.getTransform();
                final Paint originalPaint = g2d.getPaint();
                g2d.translate(offsetX, offsetY);
                if (paint != null)
                    g2d.setPaint(paint);
                g2d.fill(shape);
                g2d.setPaint(originalPaint);
                g2d.setTransform(originalAffineTransform);
            }
            
            public void moveTo(final double newBoundsCenterX,
                               final double newBoundsCenterY) {
                offsetX = newBoundsCenterX - originalBounds.getCenterX();
                offsetY = newBoundsCenterY - originalBounds.getCenterY();
            }
            
            public void moveBy(final double dx,
                               final double dy) {
                offsetX += dx;
                offsetY += dy;
            }
            
            public boolean contains(final Point2D pt) {
                return shape.contains(pt.getX() - offsetX, pt.getY() - offsetY);
            }
            
            public Point2D getTopLeft() {
                return new Point2D.Double(offsetX + originalBounds.getX(), offsetY + originalBounds.getY());
            }
            
            public Point2D getCenter() {
                return new Point2D.Double(offsetX + originalBounds.getCenterX(), offsetY + originalBounds.getCenterY()); //Like 'getTopLeft' but with adding half the size.
            }
            
            public Point2D getBottomRight() {
                return new Point2D.Double(offsetX + originalBounds.getMaxX(), offsetY + originalBounds.getMaxY()); //Like 'getTopLeft' but with adding the size of the bounds.
            }
        }
        
        public static class DrawPanel extends JPanel {
            
            private class MouseDrag extends MouseAdapter {
                private MoveableShapeHolder current;
                private Point origin;
                private Point2D center;
                
                @Override
                public void mousePressed(final MouseEvent e) {
                    current = null;
                    center = null;
                    final Point evtLoc = e.getPoint();
                    for (final MoveableShapeHolder moveable: moveables)
                        if (moveable.contains(evtLoc))
                            current = moveable; //Keep the last moveable found to contain the click point! It's important to be the last one, because the later the moveable appears in the collection, the closer to top its layer.
    
                    if (current != null) { //If a shape was clicked...
                        
                        //Initialize MouseDrag's state:
                        origin = e.getPoint();
                        center = current.getCenter();
                        
                        //Move to topmost layer:
                        moveables.remove(current); //Remove from its current position.
                        moveables.add(current); //Move to last (topmost layer).
                        
                        //Rapaint panel:
                        repaint();
                    }
                }
                
                @Override
                public void mouseDragged(final MouseEvent e) {
                    if (current != null) { //If we are dragging something (and not empty space), then:
                        current.moveTo(center.getX() + e.getX() - origin.x, center.getY() + e.getY() - origin.y);
                        repaint();
                    }
                }
    
                @Override
                public void mouseReleased(final MouseEvent e) {
                    current = null;
                    origin = null;
                    center = null;
                }
            }
            
            private final LinkedHashSet<MoveableShapeHolder> moveables;
            
            public DrawPanel() {
                moveables = new LinkedHashSet<>();
                final MouseAdapter ma = new MouseDrag();
                super.addMouseMotionListener(ma);
                super.addMouseListener(ma);
            }
            
            /**
             * Warning: all operations on the returned value must be made on the EDT.
             * @return 
             */
            public Collection<MoveableShapeHolder> getMoveables() {
                return moveables;
            }
            
            @Override
            protected void paintComponent(final Graphics g) {
                super.paintComponent(g);
                moveables.forEach(moveable -> moveable.paint((Graphics2D) g)); //Topmost moveable is painted last.
            }
            
            @Override
            public Dimension getPreferredSize() {
                if (isPreferredSizeSet())
                    return super.getPreferredSize();
                final Dimension preferredSize = new Dimension();
                moveables.forEach(moveable -> {
                    final Point2D max = moveable.getBottomRight();
                    preferredSize.width = Math.max(preferredSize.width, (int) Math.ceil(max.getX()));
                    preferredSize.height = Math.max(preferredSize.height, (int) Math.ceil(max.getY()));
                });
                return preferredSize;
            }
        }
        
        private static void createAndShowGUI() {
            final DrawPanel drawPanel = new DrawPanel();
            final Collection<MoveableShapeHolder> moveables = drawPanel.getMoveables();
            
            MoveableShapeHolder moveable = new MoveableShapeHolder(createRectangle(100, 50), Color.RED);
            moveable.moveTo(100, 75);
            moveables.add(moveable);
            
            moveable = new MoveableShapeHolder(createCircle(40), Color.GREEN);
            moveable.moveTo(125, 100);
            moveables.add(moveable);
            
            moveable = new MoveableShapeHolder(createRectangle(25, 75), Color.BLUE);
            moveable.moveTo(125, 75);
            moveables.add(moveable);
            
            final JFrame frame = new JFrame("Click to drag");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(drawPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
        
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(NonComponentMain::createAndShowGUI);
        }
    }
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.Polygon;
    import java.awt.RenderingHints;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class MoveShapes implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new MoveShapes());
        }
        
        private final DrawingPanel drawingPanel;
        
        private final ShapeModel shapeModel;
        
        public MoveShapes() {
            this.shapeModel = new ShapeModel();
            this.drawingPanel = new DrawingPanel();
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame("Move Shapes");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            frame.add(drawingPanel, BorderLayout.CENTER);
            
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
        
        public void repaint() {
            drawingPanel.repaint();
        }
        
        public class DrawingPanel extends JPanel {
    
            private static final long serialVersionUID = 1L;
            
            public DrawingPanel() {
                this.setBackground(Color.WHITE);
                this.setPreferredSize(new Dimension(600, 500));
                MoveListener listener = new MoveListener();
                this.addMouseListener(listener);
                this.addMouseMotionListener(listener);
            }
            
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(
                        RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                
                for (Shape shape : shapeModel.getShapes()) {
                    g2d.setColor(shape.getColor());
                    g2d.fillPolygon(shape.getShape());
                }
                
            }
            
        }
        
        public class MoveListener extends MouseAdapter {
            
            private Point pressedPoint;
            
            private Shape selectedShape;
    
            @Override
            public void mousePressed(MouseEvent event) {
                this.pressedPoint = event.getPoint();
                this.selectedShape = null;
                
                List<Shape> shapes = shapeModel.getShapes();
                for (int i = shapes.size() - 1; i >= 0; i--) {
                    Shape shape = shapes.get(i);
                    if (shape.getShape().contains(pressedPoint)) {
                        selectedShape = shape;
                        break;
                    }
                }
                
                if (selectedShape != null) {
                    shapes.remove(selectedShape);
                    shapes.add(selectedShape);
                }
            }
            
            @Override
            public void mouseReleased(MouseEvent event) {
                moveShape(event.getPoint());
            }
            
            @Override
            public void mouseDragged(MouseEvent event) {
                moveShape(event.getPoint());
            }
            
            private void moveShape(Point point) {
                if (selectedShape != null) {
                    int x = point.x - pressedPoint.x;
                    int y = point.y - pressedPoint.y;
                    selectedShape.incrementCenterPoint(x, y);
                    drawingPanel.repaint();
                    pressedPoint = point;
                }
            }
            
        }
        
        public class ShapeModel {
            
            private final List<Shape> shapes;
            
            public ShapeModel() {
                this.shapes = new ArrayList<>();
                this.shapes.add(new Shape(100, 250, Color.BLUE, ShapeType.TRIANGLE));
                this.shapes.add(new Shape(300, 250, Color.RED, ShapeType.RECTANGLE));
                this.shapes.add(new Shape(500, 250, Color.BLACK, ShapeType.CIRCLE));
            }
    
            public List<Shape> getShapes() {
                return shapes;
            }
            
        }
        
        public class Shape {
            
            private final Color color;
            
            private Point centerPoint;
            
            private Polygon shape;
            
            private final ShapeType shapeType;
            
            public Shape(int x, int y, Color color, ShapeType shapeType) {
                this.centerPoint = new Point(x, y);
                this.color = color;
                this.shapeType = shapeType;
                createPolygon(shapeType);
            }
            
            public void incrementCenterPoint(int x, int y) {
                centerPoint.x += x;
                centerPoint.y += y;
                createPolygon(shapeType);
            }
    
            private void createPolygon(ShapeType shapeType) {
                this.shape = new Polygon();
                
                switch (shapeType) {
                case TRIANGLE:
                    int angle = 30;
                    int radius = 100;
                    for (int i = 0; i < 3; i++) {
                        Point point = toCartesianCoordinates(angle, radius);
                        shape.addPoint(point.x, point.y);
                        angle += 120;
                    }
                    break;
                case RECTANGLE:
                    angle = 45;
                    radius = 100;
                    for (int i = 0; i < 4; i++) {
                        Point point = toCartesianCoordinates(angle, radius);
                        shape.addPoint(point.x, point.y);
                        angle += 90;
                    }
                    break;
    
                case CIRCLE:
                    radius = 75;
                    for (angle = 0; angle < 360; angle++) {
                        Point point = toCartesianCoordinates(angle, radius);
                        shape.addPoint(point.x, point.y);
                    }
                    break;
                }
            }
            
            private Point toCartesianCoordinates(int angle, int radius) {
                double theta = Math.toRadians(angle);
                int x = (int) Math.round(Math.cos(theta) * radius) + centerPoint.x;
                int y = (int) Math.round(Math.sin(theta) * radius) + centerPoint.y;
                return new Point(x, y);
            }
    
            public Color getColor() {
                return color;
            }
    
            public Point getCenterPoint() {
                return centerPoint;
            }
    
            public Polygon getShape() {
                return shape;
            }
            
        }
        
        public enum ShapeType {
            TRIANGLE, RECTANGLE, CIRCLE
        }
    
    }