1. 项目概述
最近在整理Java基础知识时,突然想实现一个简单的画图程序。这个需求看似基础,但涉及到的知识点却非常全面,从GUI编程到事件处理,再到图形绘制,正好可以系统性地复习Java的核心能力。下面我就把实现过程完整记录下来,希望能帮助到正在学习Java图形编程的朋友们。
这个画图程序主要实现以下功能:
- 基本图形绘制(直线、矩形、圆形)
- 自由画笔功能
- 颜色选择
- 图形清除
- 简单的撤销操作
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与准备
2.1 Java图形技术对比
Java中实现图形界面主要有以下几种方案:
- AWT:最基础的GUI工具包,但功能有限
- Swing:基于AWT的改进版,组件更丰富
- JavaFX:新一代图形界面技术,但学习成本较高
- 第三方库:如Processing等
考虑到我们的需求是简单的画图功能,Swing已经足够满足需求,而且它是Java标准库的一部分,无需额外安装依赖。
2.2 开发环境准备
推荐使用以下环境:
- JDK 17(长期支持版本)
- IntelliJ IDEA(社区版即可)
- 或者Eclipse
注意:确保环境变量配置正确,可以通过
java -version命令验证
3. 核心实现步骤
3.1 创建主窗口框架
java复制import javax.swing.*;
import java.awt.*;
public class DrawingApp {
public static void main(String[] args) {
JFrame frame = new JFrame("简易画图程序");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null); // 居中显示
// 后续组件将添加在这里
frame.setVisible(true);
}
}
3.2 添加绘图面板
java复制class DrawingPanel extends JPanel {
private ArrayList<Shape> shapes = new ArrayList<>();
private Color currentColor = Color.BLACK;
private Shape currentShape = null;
private int startX, startY;
public DrawingPanel() {
// 设置鼠标监听器
this.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
currentShape = new Shape(currentShapeType, currentColor);
}
public void mouseReleased(MouseEvent e) {
if(currentShape != null) {
shapes.add(currentShape);
currentShape = null;
repaint();
}
}
});
// 鼠标移动监听器
this.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
if(currentShape != null) {
currentShape.updateCoordinates(startX, startY, e.getX(), e.getY());
repaint();
}
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// 绘制所有已保存的图形
for(Shape shape : shapes) {
shape.draw(g2d);
}
// 绘制当前正在绘制的图形
if(currentShape != null) {
currentShape.draw(g2d);
}
}
}
3.3 实现Shape类
java复制class Shape {
enum Type { LINE, RECTANGLE, OVAL, FREEHAND }
private Type type;
private Color color;
private ArrayList<Point> points; // 用于自由绘制
public Shape(Type type, Color color) {
this.type = type;
this.color = color;
if(type == Type.FREEHAND) {
points = new ArrayList<>();
}
}
public void updateCoordinates(int x1, int y1, int x2, int y2) {
if(type == Type.FREEHAND) {
points.add(new Point(x2, y2));
} else {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
}
public void draw(Graphics2D g) {
g.setColor(color);
switch(type) {
case LINE:
g.drawLine(x1, y1, x2, y2);
break;
case RECTANGLE:
g.drawRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x2 - x1), Math.abs(y2 - y1));
break;
case OVAL:
g.drawOval(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x2 - x1), Math.abs(y2 - y1));
break;
case FREEHAND:
if(points.size() > 1) {
for(int i = 1; i < points.size(); i++) {
Point p1 = points.get(i-1);
Point p2 = points.get(i);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
break;
}
}
}
4. 添加工具栏功能
4.1 创建工具栏
java复制private void setupToolbar(JFrame frame) {
JToolBar toolBar = new JToolBar();
// 工具按钮
JButton lineBtn = new JButton("直线");
JButton rectBtn = new JButton("矩形");
JButton ovalBtn = new JButton("圆形");
JButton freeBtn = new JButton("自由绘制");
JButton clearBtn = new JButton("清除");
JButton undoBtn = new JButton("撤销");
// 颜色选择器
JButton colorBtn = new JButton("颜色");
colorBtn.addActionListener(e -> {
Color newColor = JColorChooser.showDialog(frame, "选择颜色", currentColor);
if(newColor != null) {
currentColor = newColor;
}
});
// 添加按钮到工具栏
toolBar.add(lineBtn);
toolBar.add(rectBtn);
toolBar.add(ovalBtn);
toolBar.add(freeBtn);
toolBar.addSeparator();
toolBar.add(colorBtn);
toolBar.addSeparator();
toolBar.add(clearBtn);
toolBar.add(undoBtn);
frame.add(toolBar, BorderLayout.NORTH);
}
4.2 实现按钮功能
java复制// 在DrawingPanel类中添加
private Type currentShapeType = Type.LINE;
public void setShapeType(Type type) {
this.currentShapeType = type;
}
public void clear() {
shapes.clear();
repaint();
}
public void undo() {
if(!shapes.isEmpty()) {
shapes.remove(shapes.size() - 1);
repaint();
}
}
// 在setupToolbar方法中为按钮添加事件监听
lineBtn.addActionListener(e -> drawingPanel.setShapeType(Shape.Type.LINE));
rectBtn.addActionListener(e -> drawingPanel.setShapeType(Shape.Type.RECTANGLE));
ovalBtn.addActionListener(e -> drawingPanel.setShapeType(Shape.Type.OVAL));
freeBtn.addActionListener(e -> drawingPanel.setShapeType(Shape.Type.FREEHAND));
clearBtn.addActionListener(e -> drawingPanel.clear());
undoBtn.addActionListener(e -> drawingPanel.undo());
5. 功能扩展与优化
5.1 添加图形填充功能
java复制// 在Shape类中添加
private boolean filled = false;
public void setFilled(boolean filled) {
this.filled = filled;
}
// 修改draw方法
switch(type) {
case RECTANGLE:
if(filled) {
g.fillRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x2 - x1), Math.abs(y2 - y1));
} else {
g.drawRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x2 - x1), Math.abs(y2 - y1));
}
break;
// 其他图形类似
}
5.2 添加线宽设置
java复制// 在DrawingPanel类中添加
private float strokeWidth = 1.0f;
public void setStrokeWidth(float width) {
this.strokeWidth = width;
}
// 修改paintComponent方法
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(strokeWidth));
5.3 添加保存功能
java复制public void saveToFile(File file) throws IOException {
BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
paintAll(g2d);
g2d.dispose();
ImageIO.write(image, "PNG", file);
}
6. 常见问题与解决方案
6.1 图形闪烁问题
当快速拖动鼠标时,可能会出现图形闪烁。解决方案是启用双缓冲:
java复制public DrawingPanel() {
setDoubleBuffered(true); // 启用双缓冲
// ...其他初始化代码
}
6.2 内存占用过高
长时间使用后,保存的图形过多可能导致内存占用高。可以:
- 限制保存的图形数量
- 定期清理旧的图形
- 将图形数据序列化到临时文件
6.3 撤销功能实现不完整
当前的撤销功能只能撤销一步操作。要实现多步撤销,可以使用:
java复制private Stack<Shape> undoStack = new Stack<>();
private Stack<Shape> redoStack = new Stack<>();
public void undo() {
if(!undoStack.isEmpty()) {
redoStack.push(undoStack.pop());
repaint();
}
}
public void redo() {
if(!redoStack.isEmpty()) {
undoStack.push(redoStack.pop());
repaint();
}
}
7. 性能优化建议
- 局部重绘:只重绘发生变化的部分区域,而不是整个面板
- 图形合并:将静态图形合并为一个图像,减少绘制调用
- 使用位图缓存:对于复杂的图形,可以先绘制到离屏位图
- 优化数据结构:使用空间分区数据结构(如四叉树)来管理图形对象
8. 项目扩展方向
- 添加更多图形类型:如多边形、曲线、文字等
- 实现图层功能:允许用户在不同图层上绘制
- 添加滤镜效果:如模糊、锐化等
- 支持导入/导出:实现常见图片格式的导入导出
- 网络协作:允许多用户同时编辑同一画布
这个简单的画图程序虽然功能基础,但涵盖了Java GUI编程的许多核心概念。通过这个项目,可以深入理解Swing组件、事件处理、图形绘制等关键技术。在实际开发中,可以根据需求进一步扩展功能,比如添加更复杂的图形算法或优化用户体验。
