拖动绘制的形状
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
。我希望有人可以提供不进行这种转换的解决方案。但是尚未提供更好的解决方案,因此我将把它留在这里,以供其他可能觉得有用的人使用。以上是关于拖动绘制的形状的主要内容,如果未能解决你的问题,请参考以下文章