You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:15:34