为何基于JPanel/JFrame的绘图程序仅显示最后绘制的图形?
Java绘图程序问题:仅显示最后添加的图形
我用JPanel和JFrame开发了一个能画直线和圆形的程序,现在遇到个问题:程序只会重绘最后添加的图形对象。我试过调整repaint()的调用位置,发现直接在paintComponent()里画的图形能正常显示并逐帧刷新,但把Graphics对象传给外部方法画的图形却显示不出来。相关代码如下:
Painter类
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Painter implements ActionListener, MouseListener, MouseMotionListener { Color temp = Color.RED; // 0 = 直线, 1 = 圆形 int object = 0; PaintingPanel canvas; Point startPoint = new Point(); Point endPoint = new Point(); Painter() { // 按钮 JButton circleBut = new JButton(); JButton lineBut = new JButton(); // 窗口设置 JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(500, 500); // 布局容器 JPanel holder = new JPanel(); JPanel leftPanel = new JPanel(); holder.setLayout(new BorderLayout()); leftPanel.setLayout(new GridLayout(3, 1)); JPanel northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1, 2)); frame.add(holder); // 圆形和直线按钮 circleBut.setText("Circle"); lineBut.setText("Line"); northPanel.add(circleBut); northPanel.add(lineBut); circleBut.setActionCommand("1"); lineBut.setActionCommand("0"); holder.add(northPanel, BorderLayout.NORTH); // 红色按钮 JButton redPaint = new JButton(); redPaint.setBackground(Color.RED); redPaint.setOpaque(true); redPaint.setBorderPainted(false); leftPanel.add(redPaint); redPaint.setActionCommand("red"); // 绿色按钮 JButton greenPaint = new JButton(); greenPaint.setBackground(Color.GREEN); greenPaint.setOpaque(true); greenPaint.setBorderPainted(false); leftPanel.add(greenPaint); greenPaint.setActionCommand("green"); // 蓝色按钮 JButton bluePaint = new JButton(); bluePaint.setBackground(Color.BLUE); bluePaint.setOpaque(true); bluePaint.setBorderPainted(false); leftPanel.add(bluePaint); bluePaint.setActionCommand("blue"); holder.add(leftPanel, BorderLayout.WEST); // 添加绘图面板到中心 canvas = new PaintingPanel(); holder.add(canvas, BorderLayout.CENTER); // 添加事件监听器 circleBut.addActionListener(this); lineBut.addActionListener(this); redPaint.addActionListener(this); greenPaint.addActionListener(this); bluePaint.addActionListener(this); canvas.addMouseListener(this); frame.setContentPane(holder); frame.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("red")) { temp = Color.RED; System.out.println("切换红色"); } else if (e.getActionCommand().equals("green")) { temp = Color.GREEN; System.out.println("切换绿色"); } else if (e.getActionCommand().equals("blue")) { temp = Color.BLUE; System.out.println("切换蓝色"); } else if (e.getActionCommand().equals("0")) { object = 0; System.out.println("切换直线工具"); } else if (e.getActionCommand().equals("1")) { object = 1; System.out.println("切换圆形工具"); } } @Override public void mouseDragged(MouseEvent e) {} @Override public void mouseMoved(MouseEvent e) {} @Override public void mouseClicked(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) { startPoint.setLocation(e.getPoint()); } @Override public void mouseReleased(MouseEvent e) { endPoint.setLocation(e.getPoint()); if (object == 0) { canvas.addPrimitive(new Line(startPoint, endPoint, temp)); } if (object == 1){ canvas.addPrimitive(new Circle(startPoint, endPoint, temp)); } canvas.repaint(); } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} public static void main(String[] args) { new Painter(); } }
PaintingPrimitive抽象类
import java.awt.Graphics; import java.awt.Color; public abstract class PaintingPrimitive{ Color color; PaintingPrimitive(Color color) { this.color = color; } // 模板方法模式示例 public final void draw(Graphics g) { g.setColor(color); drawGeometry(g); } public void setColor(Color color) { this.color = color; } protected abstract void drawGeometry(Graphics g); }
Line类
import java.awt.Graphics; import java.awt.Point; import java.awt.Color; public class Line extends PaintingPrimitive{ Point startPoint = new Point(); Point endPoint = new Point(); public Line(Point start, Point end, Color c) { super(c); this.startPoint = start; this.endPoint = end; } public void drawGeometry(Graphics g) { System.out.println("调用Line的drawGeometry"); g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y); } @Override public String toString() { return "Line"; } }
PaintingPanel类
import java.util.ArrayList; import javax.swing.JPanel; import java.awt.Graphics; import java.awt.Color; public class PaintingPanel extends JPanel { ArrayList<PaintingPrimitive> primitives = new ArrayList<PaintingPrimitive>(); PaintingPanel() { setBackground(Color.WHITE); } public void addPrimitive(PaintingPrimitive obj) { primitives.add(obj); this.repaint(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); for (PaintingPrimitive shape : primitives) { // 测试用直线,每次重绘都会显示 g.drawLine(0,0,100,100); shape.draw(g); } } }
问题原因
你在Painter类里只定义了一对startPoint和endPoint成员变量,每次鼠标操作时只是修改它们的坐标值,而不是创建新的Point对象。这导致所有添加到primitives列表中的Line/Circle对象,都共享这两个Point的引用——最后所有图形的坐标都会被最后一次绘制的坐标覆盖,所以看起来只显示最后一个图形。
修复方案
修改mousePressed和mouseReleased方法,每次都创建新的Point对象,让每个图形持有独立的坐标引用:
@Override public void mousePressed(MouseEvent e) { // 每次按下鼠标都新建Point,避免引用复用 startPoint = new Point(e.getPoint()); } @Override public void mouseReleased(MouseEvent e) { // 每次释放鼠标都新建Point endPoint = new Point(e.getPoint()); if (object == 0) { canvas.addPrimitive(new Line(startPoint, endPoint, temp)); } if (object == 1){ canvas.addPrimitive(new Circle(startPoint, endPoint, temp)); } canvas.repaint(); }
或者更直接,在创建图形时直接复制Point对象:
@Override public void mouseReleased(MouseEvent e) { if (object == 0) { // 复制起点和终点,避免引用共享 canvas.addPrimitive(new Line(new Point(startPoint), new Point(e.getPoint()), temp)); } if (object == 1){ canvas.addPrimitive(new Circle(new Point(startPoint), new Point(e.getPoint()), temp)); } canvas.repaint(); }
额外优化
可以把PaintingPanel里的测试直线g.drawLine(0,0,100,100)移除,避免每次重绘都重复绘制这条线干扰效果。
内容的提问来源于stack exchange,提问作者Cole
相关产品推荐
相关产品推荐

