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

Java Swing PaintBrush中paintComponent多色独立绘制问题

问题描述

我希望通过paintComponent实现每个绘制元素的独立渲染。
我正在开发一款PaintBrush画图程序,预期效果是不同线条可以使用不同颜色绘制,但当前所有线条会同步变成同一种颜色。我原本通过绘制白色实现橡皮擦功能,但实际使用橡皮擦时所有线条都会变成白色,切换其他颜色后所有线条又会同步变成新选中的颜色。

现有问题代码片段

颜色选择功能代码

//choose the color
public void ColorBox() {
        ArrayList<Color> clist = new ArrayList<>();
        clist.add(Color.RED);
        clist.add(Color.ORANGE);
        clist.add(Color.YELLOW);
        clist.add(Color.GREEN);
        clist.add(Color.BLUE);
        clist.add(Color.MAGENTA);
        clist.add(Color.WHITE);
        clist.add(Color.BLACK);
        
        for (int i = 0; i < clist.size(); i++) {
            JButton cbutton = new JButton();
            cbutton.setBackground(clist.get(i));
            cbutton.setBounds(500+50*i, 10, 37, 37); add(cbutton);
            cbutton.addActionListener(e -> {
                colour = cbutton.getBackground();
            });
            add(cbutton);
        }
    }

铅笔、橡皮擦与渲染逻辑代码

public void Pencil() {
        JButton pen = new JButton(new ImageIcon("icon\\pencil.png"));
        pen.setBounds(200, 10, 37, 37); add(pen);
        pen.addActionListener(e -> {
            draw = true; erase = false;
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    super.mouseDragged(e);
                    x = e.getX();
                    y = e.getY();
                    plist.add(new Point(x, y));
                    repaint();
                }
            });
        });
    }
    
    public void Eraser() {
        JButton eraser = new JButton(new ImageIcon("icon\\eraser.png"));
        eraser.setBounds(250, 10, 37, 37); add(eraser);
        eraser.addActionListener(e -> {
            draw = false; erase = true;
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    super.mouseDragged(e);
                    x = e.getX();
                    y = e.getY();
                    elist.add(new Point(x, y));
                    repaint();
                }
            });
        });
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (draw == true)
            g.setColor(colour);
            for (Point p : plist) {
                g.fillOval(p.x, p.y, 5, 5);
            }
        if (erase == true)
            g.setColor(Color.WHITE);
            for (Point p : plist) {
                g.fillOval(p.x, p.y, 5, 5);
            }
    }

完整原始代码

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Point {
    int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Board extends JPanel {
    int x, y;
    Color colour;
    boolean draw = false; boolean erase = false;
    Vector<Point> plist = new Vector<>();
    Vector<Point> elist = new Vector<>();
    public void ColorBox() {
        ArrayList<Color> clist = new ArrayList<>();
        clist.add(Color.RED);
        clist.add(Color.ORANGE);
        clist.add(Color.YELLOW);
        clist.add(Color.GREEN);
        clist.add(Color.BLUE);
        clist.add(Color.MAGENTA);
        clist.add(Color.WHITE);
        clist.add(Color.BLACK);
        
        for (int i = 0; i < clist.size(); i++) {
            JButton cbutton = new JButton();
            cbutton.setBackground(clist.get(i));
            cbutton.setBounds(500+50*i, 10, 37, 37); add(cbutton);
            cbutton.addActionListener(e -> {
                colour = cbutton.getBackground();
            });
            add(cbutton);
        }
    }
    
    public void Pencil() {
        JButton pen = new JButton(new ImageIcon("icon\\pencil.png"));
        pen.setBounds(200, 10, 37, 37); add(pen);
        pen.addActionListener(e -> {
            draw = true; erase = false;
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    super.mouseDragged(e);
                    x = e.getX();
                    y = e.getY();
                    plist.add(new Point(x, y));
                    repaint();
                }
            });
        });
    }
    
    public void Eraser() {
        JButton eraser = new JButton(new ImageIcon("icon\\eraser.png"));
        eraser.setBounds(250, 10, 37, 37); add(eraser);
        eraser.addActionListener(e -> {
            draw = false; erase = true;
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    super.mouseDragged(e);
                    x = e.getX();
                    y = e.getY();
                    elist.add(new Point(x, y));
                    repaint();
                }
            });
        });
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (draw == true)
            g.setColor(colour);
            for (Point p : plist) {
                g.fillOval(p.x, p.y, 5, 5);
            }
        if (erase == true)
            g.setColor(Color.WHITE);
            for (Point p : plist) {
                g.fillOval(p.x, p.y, 5, 5);
            }
    }
    public Board() {
        setLayout(null);
        this.Pencil();
        this.Eraser();
        this.ColorBox();
        setBackground(Color.WHITE);
    }
}

public class PaintBrush extends JFrame {
    
    public PaintBrush() {
        setSize(1200, 600);
        setTitle("그림판");
        add(new Board());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    public static void main(String[] args) {
        PaintBrush p = new PaintBrush();
    }

}
问题根因

代码存在三个核心问题导致效果不符合预期:

  • 所有绘制点只存储了坐标,没有绑定对应颜色:paintComponent每次重绘都会用当前全局选中的colour遍历所有点重新绘制,所以切换颜色时所有已画内容都会同步变色。
  • 逻辑分支未加花括号,橡皮擦逻辑错误:if (draw == true)和if (erase == true)后没有包裹代码块,Java默认只将紧跟的第一行代码作为分支内容,后续for循环无论分支是否成立都会执行;且橡皮擦分支遍历的是铅笔的plist,完全没有用到存储橡皮擦轨迹的elist,开启橡皮擦后会直接把所有铅笔轨迹绘制成白色。
  • 重复添加鼠标监听器:每次点击铅笔/橡皮擦按钮都会新增一个MouseMotionListener,拖拽时会重复触发逻辑,点击工具次数越多程序卡顿越明显。
修复方案
  • 新建DrawElement类,将每一段连续绘制的轨迹(点集合+对应颜色)作为独立对象存储,不需要单独维护橡皮擦的点列表,橡皮擦本质就是颜色为背景白色的绘制元素。
  • 修正paintComponent渲染逻辑,遍历所有存储的绘制元素,用元素自身保存的颜色绘制对应轨迹,不要用全局当前色绘制所有内容。
  • 鼠标监听器只在面板初始化时添加一次,通过当前选中的工具类型判断绘制铅笔还是橡皮擦,不要每次点击工具按钮都新增监听器。
  • 为所有if分支补全花括号,避免逻辑执行顺序不符合预期。

修复后完整代码

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

// 存储单个独立绘制元素:连续轨迹+对应颜色
class DrawElement {
    List<Point> points = new ArrayList<>();
    Color color;
    public DrawElement(Color color) {
        this.color = color;
    }
}

class Point {
    int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Board extends JPanel {
    Color currentColour = Color.BLACK;
    boolean isEraser = false;
    // 存储所有独立绘制元素
    List<DrawElement> elements = new ArrayList<>();
    // 当前正在绘制的元素
    DrawElement currentElement = null;

    public void ColorBox() {
        ArrayList<Color> clist = new ArrayList<>();
        clist.add(Color.RED);
        clist.add(Color.ORANGE);
        clist.add(Color.YELLOW);
        clist.add(Color.GREEN);
        clist.add(Color.BLUE);
        clist.add(Color.MAGENTA);
        clist.add(Color.WHITE);
        clist.add(Color.BLACK);
        
        for (int i = 0; i < clist.size(); i++) {
            JButton cbutton = new JButton();
            cbutton.setBackground(clist.get(i));
            cbutton.setBounds(500+50*i, 10, 37, 37);
            final Color selectColor = clist.get(i);
            cbutton.addActionListener(e -> {
                currentColour = selectColor;
                isEraser = false;
            });
            add(cbutton);
        }
    }
    
    public void Pencil() {
        JButton pen = new JButton(new ImageIcon("icon\\pencil.png"));
        pen.setBounds(200, 10, 37, 37);
        pen.addActionListener(e -> isEraser = false);
        add(pen);
    }
    
    public void Eraser() {
        JButton eraser = new JButton(new ImageIcon("icon\\eraser.png"));
        eraser.setBounds(250, 10, 37, 37);
        eraser.addActionListener(e -> isEraser = true);
        add(eraser);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 遍历所有已绘制元素,用元素自身绑定的颜色渲染
        for (DrawElement element : elements) {
            g.setColor(element.color);
            for (Point p : element.points) {
                g.fillOval(p.x, p.y, 5, 5);
            }
        }
    }

    public Board() {
        setLayout(null);
        setBackground(Color.WHITE);
        // 仅添加一次鼠标监听器
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (currentElement == null) {
                    // 拖拽开始时新建独立绘制元素,橡皮擦使用白色
                    Color drawColor = isEraser ? Color.WHITE : currentColour;
                    currentElement = new DrawElement(drawColor);
                    elements.add(currentElement);
                }
                currentElement.points.add(new Point(e.getX(), e.getY()));
                repaint();
            }
        });
        // 鼠标释放后重置当前绘制元素,下次拖拽生成新的独立元素
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                currentElement = null;
            }
        });

        this.Pencil();
        this.Eraser();
        this.ColorBox();
    }
}

public class PaintBrush extends JFrame {
    public PaintBrush() {
        setSize(1200, 600);
        setTitle("画图程序");
        add(new Board());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    public static void main(String[] args) {
        new PaintBrush();
    }
}

修复后每一段笔画都是独立存储的渲染单元,切换颜色不会影响已绘制的内容,橡皮擦仅会擦除拖拽经过的区域,不会将所有线条同步改为白色。

内容的提问来源于stack exchange,提问作者Kim minseong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:24:16