拖动绘制的形状

Posted

技术标签:

【中文标题】拖动绘制的形状【英文标题】:Drag a painted Shape 【发布时间】:2021-07-30 06:10:57 【问题描述】:

Shape 接口允许您使用不同的类创建形状。

在下面的代码中,我使用 3 个不同的类创建形状:

GeneralPath barBell = new GeneralPath();
barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
shapePanel.addShape(barBell, Color.RED);

shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);

Polygon triangle = new Polygon();
triangle.addPoint(0, 0);
triangle.addPoint(100, 0);
triangle.addPoint(50, 100);
triangle.translate(400, 250);
shapePanel.addShape(triangle, Color.GREEN);

生成屏幕图像:

很遗憾,Shape 接口不提供任何用于转换 Shape 的功能。

例如,要将 Shape 移动到新位置,我使用以下命令:

if (dragShape instanceof Path2D)
    ((Path2D)dragShape).transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
else if (dragShape instanceof Polygon)
    ((Polygon)dragShape).translate(deltaX, deltaY);
else if (dragShape instanceof RectangularShape)

    RectangularShape rs = (RectangularShape)dragShape;
    Rectangle r = rs.getBounds();
    r.x += deltaX;
    r.y += deltaY;
    rs.setFrame( r );

我不喜欢 instanceof 逻辑。

是否有更通用的方法可以在不使用instanceof 逻辑的面板周围拖动任何Shape

完整示例:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

public class ShapePanel2 extends JPanel

    private ArrayList<ColoredShape> coloredShapes = new ArrayList<ColoredShape>();

    public ShapePanel2()
    
        DragListener dl = new DragListener();
        addMouseListener(dl);
        addMouseMotionListener(dl);
    

    @Override
    protected void paintComponent(Graphics g)
    
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        for (ColoredShape cs : coloredShapes)
        
            g2.setColor( cs.getForeground() );
            g2.fill( cs.getShape() );
        
    

    @Override
    public Dimension getPreferredSize()
    
        return new Dimension(600, 500);
    

    public void addShape(Shape shape, Color color)
    
        ColoredShape cs = new ColoredShape(color, shape);
        coloredShapes.add( cs );
        repaint();
    

    class ColoredShape
    
        private Color foreground;
        private Shape shape;

        public ColoredShape(Color foreground, Shape shape)
        
            this.foreground = foreground;
            this.shape = shape;
        

        public Color getForeground()
        
            return foreground;
        

        public void setForeground(Color foreground)
        
            this.foreground = foreground;
        

        public Shape getShape()
        
            return shape;
        
    

    class DragListener extends MouseAdapter
    
        private Shape dragShape;
        private Point pressed;

        @Override
        public void mousePressed(MouseEvent e)
        
            if (SwingUtilities.isLeftMouseButton(e))
            
                pressed = e.getPoint();

                for (int i = coloredShapes.size() - 1; i >= 0; i--)
                
                    ColoredShape cs = coloredShapes.get(i);

                    if (cs.getShape().contains( pressed ))
                    
                        coloredShapes.remove(i);
                        coloredShapes.add(cs);
                        repaint();
                        dragShape = cs.getShape();
                        break;
                    
                
            
        

        @Override
        public void mouseDragged(MouseEvent e)
        
            if (dragShape != null)
            
                int deltaX = e.getX() - pressed.x;
                int deltaY = e.getY() - pressed.y;

                if (dragShape instanceof Path2D)
                    ((Path2D)dragShape).transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
                else if (dragShape instanceof Polygon)
                    ((Polygon)dragShape).translate(deltaX, deltaY);
                else if (dragShape instanceof RectangularShape)
                
                    RectangularShape rs = (RectangularShape)dragShape;
                    Rectangle r = rs.getBounds();
                    r.x += deltaX;
                    r.y += deltaY;
                    rs.setFrame( r );
                

                pressed = e.getPoint();
                repaint();
            
        

        @Override
        public void mouseReleased(MouseEvent e)
        
            dragShape = null;
        
    

    private static void createAndShowGUI()
    
        ShapePanel2 shapePanel = new ShapePanel2();

        GeneralPath barBell = new GeneralPath();
        barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
        barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
        barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
        shapePanel.addShape(barBell, Color.RED);

        shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);

        Polygon triangle = new Polygon();
        triangle.addPoint(0, 0);
        triangle.addPoint(100, 0);
        triangle.addPoint(50, 100);
        triangle.translate(400, 250);
        shapePanel.addShape(triangle, Color.GREEN);

        JFrame frame = new JFrame("ShapePanel2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(shapePanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    

    public static void main(String[] args)
    
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    

【问题讨论】:

【参考方案1】:

一种方法可能是将每个Shape 转换为GeneralPath,因为Shape 已添加到面板中。

现在拖拽逻辑只需要支持一个实现Shape接口的类:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

public class ShapePanel extends JPanel

    private ArrayList<ColoredShape> coloredShapes = new ArrayList<ColoredShape>();

    public ShapePanel()
    
        DragListener dl = new DragListener();
        addMouseListener(dl);
        addMouseMotionListener(dl);
    

    @Override
    protected void paintComponent(Graphics g)
    
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        for (ColoredShape cs : coloredShapes)
        
            g2.setColor( cs.getForeground() );
            g2.fill( cs.getShape() );
        
    

    @Override
    public Dimension getPreferredSize()
    
        return new Dimension(600, 500);
    

    public void addShape(Shape shape, Color color)
    
        // Convert the Shape to a GeneralPath so the Shape can be translated
        // to a new location when dragged

        ColoredShape cs = new ColoredShape(color, new GeneralPath(shape));
        coloredShapes.add( cs );
        repaint();
    

    class ColoredShape
    
        private Color foreground;
        private GeneralPath shape;

        public ColoredShape(Color foreground, GeneralPath shape)
        
            this.foreground = foreground;
            this.shape = shape;
        

        public Color getForeground()
        
            return foreground;
        

        public void setForeground(Color foreground)
        
            this.foreground = foreground;
        

        public GeneralPath getShape()
        
            return shape;
        
    

    class DragListener extends MouseAdapter
    
        private GeneralPath dragShape;
        private Point pressed;

        @Override
        public void mousePressed(MouseEvent e)
        
            //  the clicked Shape will be moved to the end of the List
            //  so it is painted on top of all other shapes.

            if (SwingUtilities.isLeftMouseButton(e))
            
                pressed = e.getPoint();

                for (int i = coloredShapes.size() - 1; i >= 0; i--)
                
                    ColoredShape cs = coloredShapes.get(i);

                    if (cs.getShape().contains( pressed ))
                    
                        coloredShapes.remove(i);
                        coloredShapes.add(cs);
                        repaint();
                        dragShape = cs.getShape();
                        break;
                    
                
            
        

        @Override
        public void mouseDragged(MouseEvent e)
        
            if (dragShape != null)
            
                int deltaX = e.getX() - pressed.x;
                int deltaY = e.getY() - pressed.y;

                dragShape.transform(AffineTransform.getTranslateInstance(deltaX, deltaY));

                pressed = e.getPoint();
                repaint();
            
        

        @Override
        public void mouseReleased(MouseEvent e)
        
            dragShape = null;
        
    

    private static void createAndShowGUI()
    
        ShapePanel shapePanel = new ShapePanel();

        GeneralPath barBell = new GeneralPath();
        barBell.append(new Ellipse2D.Double(100, 100, 75, 100), true);
        barBell.append(new Rectangle2D.Double(150, 125, 175, 50), true);
        barBell.append(new Ellipse2D.Double(300, 100, 75, 100), true);
        shapePanel.addShape(barBell, Color.RED);

        shapePanel.addShape(new Rectangle2D.Double(100, 300, 75, 150), Color.BLUE);

        Polygon triangle = new Polygon();
        triangle.addPoint(0, 0);
        triangle.addPoint(100, 0);
        triangle.addPoint(50, 100);
        triangle.translate(400, 250);
        shapePanel.addShape(triangle, Color.GREEN);

        JFrame frame = new JFrame("ShapePanel");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(shapePanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    

    public static void main(String[] args)
    
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    

【讨论】:

此解决方案将所有Shape 实例转换为GeneralPath。我希望有人可以提供不进行这种转换的解决方案。但是尚未提供更好的解决方案,因此我将把它留在这里,以供其他可能觉得有用的人使用。

以上是关于拖动绘制的形状的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL用鼠标拖动绘制矩形?

拖动 QML 形状的坐标

可拖动图像上的画布绘图

如何在 C# 中拖动和移动形状

如何在android中动态地使形状可拖动和调整大小

KonvaJS:如何用箭头连接两个形状?