Java 绘图工具开发技术笔记
一、需求分析与架构设计
1.1 功能需求拆解
|
功能项 |
具体要求 |
|
图形绘制 |
支持直线、矩形、椭圆、三角形、曲线五种基本图形 |
|
颜色切换 |
提供红色、蓝色两种颜色选择 |
|
图形持久化 |
窗口重绘后图形不消失 |
|
用户交互 |
直观的按钮操作,鼠标绘制图形 |
1.2 技术架构设计
采用 MVC 架构模式:
模型层:Shapes类(存储图形数据)
视图层:MPanel(绘图面板)、DrawUI(界面布局)
控制层:DrawListener(事件处理)
二、模型层实现详解
2.1 Shapes 类设计
2.1.1 数据结构
public class Shapes {
public String name; // 图形类型
public int x1, y1, x2, y2; // 起点和终点坐标
public int x3, y3; // 三角形第三点坐标
public Color color; // 图形颜色
// 自绘制方法
public void drawShape(Graphics g) { ... }
}
2.1.2 坐标处理策略
|
图形类型 |
坐标处理逻辑 |
|
直线 |
直接连接 (x1,y1) 和 (x2,y2) |
|
矩形 |
左上角坐标为 (min (x1,x2), min (y1,y2)),宽高为 abs (x2-x1) 和 abs (y2-y1) |
|
椭圆 |
外接矩形与矩形相同,使用 drawOval 方法绘制 |
|
三角形 |
连接三个点形成封闭图形 |
|
曲线 |
由多个短直线段组成,每段连接前一点和当前点 |
三、视图层实现详解
3.1 MPanel 类(绘图面板)
3.1.1 核心功能
- 继承关系:
public class MPanel extends JPanel { ... }
3.1.2 图形持久化实现
- 关键依赖:依赖控制器中的shapeArr数组
- 数据流向:控制器更新数组 → 面板重绘时读取数组
四、控制层实现详解
4.1 DrawListener 类
4.1.1 事件处理接口
public class DrawListener implements
MouseListener, MouseMotionListener, ActionListener { ... }
4.1.2 核心状态变量
|
变量名 |
类型 |
作用 |
|
shapeArr |
Shapes[] |
存储所有图形的数组 |
|
index |
int |
当前图形索引 |
|
currentShape |
String |
当前选择的图形类型 |
|
currentColor |
Color |
当前选择的颜色 |
|
flag |
int |
三角形绘制状态标志(1 - 绘制第一条边,2 - 等待第三点) |
五、核心技术问题解决方案
5.1 窗口重绘时图形消失问题
5.1.1 问题根源分析
- Swing 重绘机制:
- 窗口状态变化时,系统自动调用paint方法
- 默认paint方法只绘制当前时刻的静态内容
- 数据丢失原因:
- 未将图形数据存储在持久化结构中
- 每次重绘时从空白状态开始
5.1.2 解决方案
- 数据存储方案:
|
// DrawListener类中 public Shapes[] shapeArr = new Shapes[1000]; |
- 保存图形逻辑
|
private void saveShape(String type, int x1, int y1, int x2, int y2, int x3, int y3) { Shapes shape = new Shapes(); shape.name = type; shape.x1 = x1; shape.y1 = y1; shape.x2 = x2; shape.y2 = y2; shape.x3 = x3; shape.y3 = y3; shape.color = currentColor; shapeArr[index++] = shape; } |
- 重绘恢复逻辑:
|
// MPanel类的paint方法 @Override public void paint(Graphics g) { super.paint(g); for (Shapes shape : shapeArr) { if (shape != null) { shape.drawShape(g); } } } |
5.1.3 技术要点
- 数组作为存储容器:
- 优点:简单直接,无需额外依赖
- 缺点:固定大小,需处理溢出问题
- 数据流向控制:
|
sequenceDiagram participant 用户 participant DrawListener participant MPanel
用户->>DrawListener: 绘制图形 DrawListener->>DrawListener: 创建图形对象 DrawListener->>DrawListener: 添加到shapeArr DrawListener->>MPanel: 调用repaint() MPanel->>DrawListener: 获取shapeArr MPanel->>Shapes: 调用drawShape() |
六、三角形分步绘制实现
6.1 状态管理机制
- 状态变量:
|
private int flag = 1; // 三角形绘制状态标志 |
- 状态转换:
|
stateDiagram [*] --> 选择三角形 选择三角形 --> 绘制第一条边: flag=1 绘制第一条边 --> 等待第三点: flag=2 等待第三点 --> 完成绘制: 鼠标点击 完成绘制 --> [*]: flag=1 |
6.2 关键代码实现
- 第一步:绘制第一条边:
|
@Override public void mouseReleased(MouseEvent e) { if ("三角形".equals(currentShape) && flag == 1) { saveShape("三角形", x1, y1, x2, y2, 0, 0); flag = 2; // 切换到等待第三点状态 } } |
- 第二步:确定第三点:
|
@Override public void mouseClicked(MouseEvent e) { if ("三角形".equals(currentShape) && flag == 2) { // 更新最后一个三角形的第三点坐标 shapeArr[index - 1].x3 = e.getX(); shapeArr[index - 1].y3 = e.getY(); flag = 1; // 重置标志 } } |
七、曲线平滑绘制实现
7.1 算法原理
- 曲线分解:
- 将曲线分解为多个短直线段
- 每段连接前一点和当前点
- 视觉效果:
- 短直线段足够密集时,视觉上形成平滑曲线
7.2 关键代码
|
@Override public void mouseDragged(MouseEvent e) { if ("曲线".equals(currentShape)) { x2 = e.getX(); y2 = e.getY();
// 保存当前线段 saveShape("曲线", x1, y1, x2, y2, 0, 0);
// 更新起点为当前终点,形成连续线段 x1 = x2; y1 = y2;
// 实时重绘 drawPanel.repaint(); } } |
八、程序运行流程
8.1 初始化阶段
- 创建主窗口和组件
- 初始化事件监听器
- 设置布局和初始状态
8.2 用户交互阶段
- 选择图形类型和颜色
- 在绘图区域进行鼠标操作
- 监听器捕获并处理事件
8.3 图形绘制阶段
- 创建图形对象
- 添加到图形数组
- 触发面板重绘
8.4 窗口重绘阶段
- 系统调用paint方法
- 从数组获取图形数据
- 重新绘制所有图形
更多推荐

所有评论(0)