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
相关产品推荐
相关产品推荐

