
绘图工具设计实践前言该项目基于Java图形界面的开发设计出一款能实现简易画图功能的工具。包含直线、矩形、等腰三角形、普通三角形、多边形以及曲线。并能够实现颜色的调用以及不同颜色的转换使用。其中多边形、曲线是在原有三角形绘制逻辑的基础上自主扩展的。在项目实现的过程中完成了从固定图形到自定义图形、任意图形的转变升级。关键概念认识多接口的实现MouseListener、ActionListener、MousemotionListener监听器 DrawListener5 同时实现3个接口必须重写接口所有抽象方法。1 ActionListener 监听按钮点击执行 actionPerformed2MouseListener 监听鼠标按下、松开、单击3) MousemotionListener:监听鼠标拖动、鼠标移动事件publicclassDrawListener3implementsMouseListener,ActionListenerMouseMotionListener{}定义成员变量publicGraphicsgr;// 绘图画笔对象publicintx1,x2,y1,y2,x3,y3,x4,y4;// 存储鼠标坐标值publicStringname;// 记录当前选中绘图模式publicColorselectColorColor.BLUE;// 当前画笔颜色publicintstrokeSize4;//画笔线条粗细publicbooleanflagtrue;// 布尔标记控制三角形绘图流程按钮监听方法:点击按钮获取按钮文字切换绘图模式或切换颜色publicvoidactionPerformed(ActionEvente){//获取按钮的内容namee.getActionCommand();System.out.println(点击按钮namename);if(蓝色.equals(name)){colorColor.BLUE;}if(红色.equals(name)){colorColor.RED;}if(粗线.equals(name)){strokeSize9;}if(细线.equals(name)){strokeSize2;}if(橡皮擦.equals(name)){}flagtrue;Graphics2Dg2(Graphics2D)gr;g2.setStroke(newBasicStroke(strokeSize));//将粗细设置应用到画笔对象使线条粗细设置真正生效}mousePressed、mouseReleased1mousePressed 鼠标按下记录起点坐标2mouseReleased 鼠标松开记录终点执行图形绘制。publicvoidmousePressed(MouseEvente){System.out.println(按下);if(flagtrue){x1e.getX();y1e.getY();}if(曲线.equals(name)){x4x1;y4y1;}//在鼠标按下和拖动时为橡皮擦增加了专属的坐标记录、颜色强制和粗线条设置if(橡皮擦.equals(name)){x4e.getX();y4e.getY();Graphics2Dg2(Graphics2D)gr;g2.setStroke(newBasicStroke(15));x1x4;y1y4;}}publicvoidmouseReleased(MouseEvente){System.out.println(松开);if(flagtrue){x2e.getX();y2e.getY();System.out.println(x2x2 y2y2);}mouseClicked 单击采集第三个顶点普通三角形逻辑拖拽生成底边单击画布获取第三个点闭合三角形publicvoidmouseClicked(MouseEvente){x3e.getX();y3e.getY();if(三角形.equals(name)){gr.drawLine(x1,y1,x3,y3);gr.drawLine(x2,y2,x3,y3);flagtrue;}}gr.setColor(selectColor) :设置画笔颜色所有图形绘制前执行保证使用选中的红/蓝色画笔。Math.abs()、Math.min()1)Math.abs() 坐标差值取正数保证宽高不为负数2)Math.min() 获取左上角坐标支持从右往左反向拖拽画图boolean flag 布尔标记(也可用整形标记控制普通三角形绘图拖拽底边时flagtrue绘制底边后置flagfalse等待单击采集第三个顶点绘制完成重置flagtrue。9.MouseDragged拖拽事件曲线的绘制不依靠单击两点此时需要使用MouseDragged拖拽事件不断地采集坐标把上一个点和当前点的位置连成一条长度很短的线无数的短线拼接形成曲线。public void mouseDragged(MouseEvent e){ //展示直线的拖拽效果 //核心视觉升级直线与矩形的“拖拽预览” 这是最大的改动。旧版在拖拽时无法看到图形的实时大小新版通过“画白线擦除旧图形 - 画新图形”的机制实现了动态预览。 核心视觉升级直线与矩形的“拖拽预览” 这是最大的改动。旧版在拖拽时无法看到图形的实时大小新版通过“画白线擦除旧图形 - 画新图形”的机制实现了动态预览。 核心视觉升级直线与矩形的“拖拽预览” 这是最大的改动。旧版在拖拽时无法看到图形的实时大小新版通过“画白线擦除旧图形 - 画新图形”的机制实现了动态预览。 // 新增直线的拖拽预览 if(直线.equals(name)){ gr.setColor(Color.WHITE); gr.drawLine(x1, y1, x4, y4); // 用白色覆盖上一次的线 gr.setColor(selectColor); gr.drawLine(x1, y1, e.getX(), e.getY()); // 绘制新线 x4 e.getX(); y4 e.getY(); // 更新上一个点的坐标 } // 新增矩形的拖拽预览 if(矩形.equals(name)){ gr.setColor(Color.WHITE); // 计算并擦除上一次的矩形 int w_old Math.abs(x4 - x1); int h_old Math.abs(y4 - y1); int X1_old Math.min(x1, x4); int Y1_old Math.min(y1, y4); gr.drawRect(X1_old, Y1_old, w_old, h_old); gr.setColor(selectColor); // 计算并绘制新矩形 int w Math.abs(e.getX() - x1); int h Math.abs(e.getY() - y1); int X1 Math.min(x1, e.getX()); int Y1 Math.min(y1, e.getY()); gr.drawRect(X1, Y1, w, h); x4 e.getX(); y4 e.getY(); // 更新上一个点的坐标 } if(曲线.equals(name)){ gr.drawLine(x4,y4,e.getX(),e.getY()); x4e.getX(); y4e.getY(); } else if(橡皮擦.equals(name)){//临时修改颜色为白色,橡皮擦的独立拖拽逻辑强制使用白色进行覆盖涂抹 gr.setColor(Color.WHITE); gr.drawLine(x1,y1,x4,y4); gr.setColor(color); x4e.getX(); y4e.getY(); } }程序开发过程1.先搭建JFrame窗口界面放置面板、功能按钮、颜色组件、线条粗细调节按钮完成基础面板的设置与布局。2.创建监听器类实现三接口并定义全局的画笔、坐标、模式名以及全局变量。3.编写按钮监听器方法点击按钮切换图形的绘制模式。4.利用flag布尔整形int),分鼠标按下与松开两个阶段控制三角形的绘图。5.在成功绘制三角形的基础上进行扩展增加多点存储实现自定义绘制多边形加入双击判断在绘制的最后以双击鼠标完成图形闭合。6.新增拖拽事件编写拖动直线与矩形和自定义曲线手绘功能与此同时增加橡皮擦功能利用白色粗线条覆盖原有画面。创意更新与改进在完成基础绘图逻辑后为了提升工具的实用性与用户体验对程序进行了以下三项核心改进1.线条粗细控制的真正生效旧版中点击“粗线/细线”按钮仅修改了 strokeSize 变量并未真正应用到画笔对象上。改进后在 actionPerformed 方法中增加了 Graphics2D 的强制转换并通过 g2.setStroke(new BasicStroke(strokeSize)) 将粗细设置实时应用到画笔使粗细切换功能真正生效。2.橡皮擦逻辑的独立与强化旧版的橡皮擦逻辑较为简单且与曲线逻辑耦合。改进后在 mousePressed 中为橡皮擦增加了专属的坐标记录x4, y4与粗线条设置宽度为15在 mouseDragged 中为其增加了独立的拖拽逻辑强制使用白色画笔进行覆盖涂抹使橡皮擦的交互更加流畅自然。3.核心视觉升级直线与矩形的“拖拽预览”效果旧版在拖拽绘制直线和矩形时用户无法在松开鼠标前看到图形的实时大小与位置。改进后在 mouseDragged 方法中引入了“画白线擦除旧图形 - 画新图形”的动态预览机制。通过不断记录上一个点的坐标x4, y4在每次拖动时先用背景色白色覆盖上一次的图形再绘制当前状态的新图形实现了所见即所得的“橡皮筋”动态预览效果。代码实践展示1.DrawListener类package Drawtoolfive; import java.awt.*; import java.awt.event.*; /** * author jinhong * date 2026/7/12 20:53 * description */ public class DrawListener5 implements MouseListener, ActionListener, MouseMotionListener { public Graphics gr; public int x1, x2, x3, y1, y2, y3,x4,y4; public String name; public boolean flag true; public int strokeSize4; public Color colorColor.BLUE; public void actionPerformed(ActionEvent e) { //获取按钮的内容 name e.getActionCommand(); System.out.println(点击按钮name name); if (蓝色.equals(name)) { color Color.BLUE; } if (红色.equals(name)) { color Color.RED; } if(粗线.equals(name)){ strokeSize9; } if(细线.equals(name)){ strokeSize2; } if(橡皮擦.equals(name)){ } flag true; Graphics2D g2(Graphics2D)gr; g2.setStroke(new BasicStroke(strokeSize)); } public void mouseClicked(MouseEvent e) { x3 e.getX(); y3 e.getY(); if (三角形.equals(name)) { gr.drawLine(x1, y1, x3, y3); gr.drawLine(x2, y2, x3, y3); flag true; } if(多边形.equals(name)){ gr.drawLine(x2,y2,x3,y3); x2x3; y2y3; if( e.getClickCount()2) { gr.drawLine(x1, y1, x3, y3); flag true; } } } public void mousePressed(MouseEvent e) { System.out.println(按下); if (flag true) { x1 e.getX(); y1 e.getY(); } if (曲线.equals(name)){ x4x1; y4y1; } if(橡皮擦.equals(name)){ x4e.getX(); y4e.getY(); Graphics2D g2(Graphics2D)gr; g2.setStroke(new BasicStroke(15)); x1x4; y1y4; } } public void mouseReleased(MouseEvent e) { System.out.println(松开); if (flagtrue) { x2 e.getX(); y2 e.getY(); System.out.println(x2 x2 y2 y2); } gr.setColor(color); int w Math.abs(x2 - x1); int h Math.abs(y2 - y1); int X1 Math.min(x1, x2); int Y1 Math.min(y1, y2); if (等腰三角形.equals(name)) { int RX 2 * x1 - x2; gr.drawLine(x1, y1, x2, y2); gr.drawLine(x1, y1, RX, y2); gr.drawLine(x2, y2, RX, y2); } if (椭圆.equals(name)) { gr.drawOval(X1, Y1, w, h); } if (三角形.equals(name)||多边形.equals(name) flagtrue) { gr.drawLine(x1, y1, x2, y2); flag false; } } public void mouseDragged(MouseEvent e){ //展示直线的拖拽效果 if (直线.equals(name)) { gr.setColor(Color.WHITE); gr.drawLine(x1, y1, x4,y4); x4e.getX(); y4e.getY(); gr.setColor(color); gr.drawLine(x1,y1,x4,y4); } int w Math.abs(x2 - x1); int h Math.abs(y2 - y1); int X1 Math.min(x1, x2); int Y1 Math.min(y1, y2); if (矩形.equals(name)) { gr.setColor(Color.WHITE); gr.drawRect(X1, Y1, w, h); x2e.getX(); y2e.getY(); w Math.abs(x2 - x1); h Math.abs(y2 - y1); X1 Math.min(x1, x2); Y1 Math.min(y1, y2); gr.setColor(color); gr.drawRect(X1, Y1, w, h); } if(曲线.equals(name)){ gr.drawLine(x4,y4,e.getX(),e.getY()); x4e.getX(); y4e.getY(); } else if(橡皮擦.equals(name)){//临时修改颜色为白色 gr.setColor(Color.WHITE); gr.drawLine(x1,y1,x4,y4); gr.setColor(color); x4e.getX(); y4e.getY(); } } public void mouseMoved(MouseEvent e){ } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } }2.DrawUI类package Drawtoolfour; import Drawtoolthree.DrawListener3; import javax.swing.*; import java.awt.*; public class DrawUI4 { public void initUI() { JFrame jf new JFrame(); jf.setSize(800, 800); jf.setTitle(画图工具); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(3); //设置窗体背景色 // jf.getContentPane().setBackground(Color.WHITE); //流式布局管理器 // FlowLayout flownew FlowLayout(); //jf.setLayout(flow); //设置边界布局管理器 BorderLayout bordernew BorderLayout(); jf.setLayout(border); //面板组件 JPanel northPanelnew JPanel(); northPanel.setBackground(Color.PINK); Dimension dmnew Dimension(0,60); northPanel.setPreferredSize(dm); jf.add(northPanel,BorderLayout.NORTH); DrawListener5 listenernew DrawListener5(); //新改动后功能按钮 //把jf.改成northPanel. //用数组简化原有设置 String[]arr{直线,矩形,等腰三角形,椭圆,三角形,多边形,曲线,橡皮擦,粗线,细线,蓝色,红色}; for (int i0;iarr.length;i){ JButton jbunew JButton(arr[i]); northPanel.add(jbu); jbu.addActionListener(listener); } JPanel drawPanelnew JPanel(); drawPanel.setBackground(Color.WHITE); jf.add(drawPanel,BorderLayout.CENTER); jf.setVisible(true); Graphics g drawPanel.getGraphics(); drawPanel.addMouseListener(listener); drawPanel.addMouseMotionListener(listener); listener.gr g; } //旧功能按钮与监听器的设置 /* JButton jbunew JButton(直线); jf.add(jbu); JButton jbu2new JButton(矩形); jf.add(jbu2); JButton jbu3new JButton(等腰三角形); jf.add(jbu3); JButton jbu4new JButton(椭圆); jf.add(jbu4); JButton jbu5new JButton(三角形); jf.add(jbu5); JButton jbu6new JButton(多边形); jf.add(jbu6); JButton jbu7new JButton(曲线); jf.add(jbu7); JButton jbu8new JButton(橡皮擦); jf.add(jbu8); JButton jbu9new JButton(粗线); jf.add(jbu9); JButton jbu10new JButton(细线); jf.add(jbu10); JButton bluenew JButton(蓝色); jf.add(blue); JButton rednew JButton(红色); jf.add(red); jf.setVisible(true); Graphics g jf.getGraphics(); DrawListener4 listener new DrawListener4(); jf.addMouseListener(listener); jf.addMouseMotionListener(listener); jbu.addActionListener(listener); jbu2.addActionListener(listener); jbu3.addActionListener(listener); jbu4.addActionListener(listener); jbu5.addActionListener(listener); jbu6.addActionListener(listener); jbu7.addActionListener(listener); jbu8.addActionListener(listener); jbu9.addActionListener(listener); jbu10.addActionListener(listener); blue.addActionListener(listener); red.addActionListener(listener); listener.gr g; } */ public static void main(String[]args){ DrawUI4 uinew DrawUI4(); ui.initUI(); } }注意事项与反思1.该界面功能的实现分为多个步骤复制增加功能要做好对应软件包类的区分。切忌将要增加的内容加入到原有的包中。分层存放不同功能的类文件可以使代码结构更清晰便于后续的调试及修改。新增绘图时也不会导致原有代码程序的混乱也便于排查代码的报错位置。2.在绘制任意三角形时曾出现画完三角形后不能继续取起点的问题其根源就是没有复位flag的位置。若未如此操作则会导致无法进行下一步的绘图工作。3.编写代码时常需对部分代码检查及修改极为容易导致大括号错乱、程序无法正常运行。编写完成后我们需要点击IDEA左上角四条横杠找到代码、重新设置代码格式调整大括号放置位置。4.橡皮擦功能可以等效为绘制白色粗曲线若直接调用橡皮擦功能可能会导致图画取与面板区产生色差这时需要在DrawUI上增加jf.getContentPane().setBackground(Color.WHITE);代码控制橡皮擦画笔颜色也为与面板一样的白色。项目运行结果展示1.基础图形的绘制及颜色的转换2.粗/细线的调换3.橡皮擦功能的实现项目总结通过本次绘图工具设计的实践我理解了Swing图形界面的开发逻辑进一步掌握接口、监听器、鼠标事件、按钮事件的使用方法不断地将思维延伸从绘制直线、矩形、椭圆、等腰三角形开始到一步步探索实现普通三角形、多边形以及曲线的绘制并学会使用布尔flag标记控制程序的运行流程不依赖复杂的语法。同时理解了如何在系统未报错的情况下通过运行输出语句找出代码中的问题、如何重新设置代码格式使得大括号有序对应代码更具逻辑性。