Java Swing实现简单画图工具
Java Swing实现一个简易画图工具直线矩形三角形多边形前言本篇将使用Swing制做一个简易画图工具能够画直线、矩形、三角形、多边形以及调节颜色、笔刷粗细等基础功能。环境JDK 17IntelliJ IDEA纯 Java 项目未使用 Maven一、整体设计程序分成两个类DrawUI负责搭界面窗口、按钮、文本框并把画笔和监听器接起来DrawListener同时实现鼠标和按钮两种监听负责记录坐标和按图形绘制。二、界面与绑定DrawUI2.1 界面组件窗口里放四类东西//图形按钮JButtonjbunewJButton(直线);jf.add(jbu);JButtonjbu2newJButton(矩形);jf.add(jbu2);JButtonjbu3newJButton(三角形);jf.add(jbu3);JButtonjbu4newJButton(多边形);jf.add(jbu4);//颜色按钮JButtonrednewJButton(红);jf.add(red);JButtongreennewJButton(绿);jf.add(green);JButtonbluenewJButton(蓝);jf.add(blue);JButtonblacknewJButton(黑);jf.add(black);//笔刷粗细输入框默认粗细为1JTextFieldwidthnewJTextField(1,5);jf.add(width);2.2 画笔直接从窗体拿并转成 Graphics2Djf.setVisible(true);//必须先设置为可见才能从这个窗体上获取画笔Graphics2Dg(Graphics2D)jf.getGraphics();//向下转型才能用setStroke为什么必须要转化为Graphics2D因为设置线宽的setStroke只在Graphics2D里才有。其中Graphics2D是Graphics的子类转过去后画线的drawLine等通过继承仍然可以使用。2.3 绑定监听器 传递画笔和文本框DrawListenerlistenernewDrawListener();jf.addMouseListener(listener);//为窗体添加鼠标监听器所有绘画操作均发生在窗体jbu.addActionListener(listener);//为各按钮添加动作监听器处理按键点击...//其余按钮同理listener.widthwidth;//把线宽输入框的输入内容引用传递给监听器listener.grg;//将画笔交给监听器这里就是事件源组件/ 监听器 / 绑定三要素组件产生动作监听器负责处理addXxxListener将两者接起来。三、事件处理DrawListener3.1 一个类实现两个接口publicclassDrawListenerimplementsMouseListener,ActionListener{//同时实现两个接口就无需再使用两个类增加程序复杂度了publicGraphics2Dgr;//画笔publicintx1,y1,x2,y2,x3,y3;//坐标publicintmode0;//0直线 1矩形 2三角形 3多边形publicintflag0;//标记位控制绘制阶段publicColorcolorColor.BLACK;//默认颜色黑色publicJTextFieldwidth;//线宽输入框一个类可以同时实现多个接口所以既接按钮点击、又接鼠标动作。3.2 按钮切换图形和颜色actionPerformedpublicvoidactionPerformed(ActionEvente){Stringnamee.getActionCommand();//获取按钮上的文字switch(name){case直线:mode0;break;//根据不同按钮做出不同响应影响后续坐标处理case矩形:mode1;break;case三角形:mode2;break;case多边形:mode3;break;case红:colorColor.RED;break;//颜色按钮设定画笔颜色case绿:colorColor.GREEN;break;case蓝:colorColor.BLUE;break;case黑:colorColor.BLACK;break;}flag0;//切换图形时把标记位清0避免残留}按钮名不同图形名 / 颜色名所以放同一个switch里区分。3.3 鼠标事件三个方法、固定顺序注意一次按下再松开会触发顺序固定mousePressed → mouseReleased → mouseClicked所以不能让三个方法都去写同一组坐标否则会覆盖掉前面记录的坐标。于是对各按键进行分工mousePressed记录起点mouseReleased记录终点坐标同时触发绘图函数mouseClicked处理多点图形后续的点。四、记录 分发的架构鼠标事件只负责记录坐标真正怎么画统一交给draw()它按mode分发publicvoiddraw(){gr.setColor(color);//绘图前设置当前颜色intwid1;//绘图前读取当前线宽try{widInteger.parseInt(width.getText().trim());//尝试读取线宽输入框输入并转化为int类型}catch(Exceptionex){//如果失败则将线宽设为默认值1wid1;}if(wid1)wid1;gr.setStroke(newBasicStroke(wid));//设置线宽if(mode0){...}//直线if(mode1){...}//矩形if(mode2){...}//三角形if(mode3){...}//多边形}好处加新图形只需要在draw()里多一个if不用动鼠标事件。4.1 直线、矩形两点图形按下记起点、松开记终点直接画// mousePressed: x1 e.getX(); y1 e.getY();// mouseReleased: x2 e.getX(); y2 e.getY(); draw();if(mode0){gr.drawLine(x1,y1,x2,y2);}if(mode1){//用 min 取左上角用abs保证宽高为正gr.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));}4.2 三角形拖动底边 点一下补第三点用flag分两个阶段flag0等底边flag1等第三点。// mousePressed (flag0):记底边起点// mouseReleased(flag0):记底边终点,draw(),flag更新为1// mouseClicked (flag1):记第三点,draw(),flag重置为0if(mode2){if(flag0)gr.drawLine(x1,y1,x2,y2);//底边if(flag1){//两条侧边gr.drawLine(x1,y1,x3,y3);gr.drawLine(x2,y2,x3,y3);}}4.3 多边形拖动第一条边 依次点双击闭合用两组坐标迭代每来一个新点就和上一个点连一段始终记住第一个点最后闭合。x1,y1 第一个点固定x2,y2 上一个点不断更新x3,y3 当前新点//mouseReleased(flag0):画第一条边,然后点二继承点三x2x3;y2y3;flag1//mouseClicked(flag1):画最新一段,再x2x3;y2y3;//若e.getClickCount()2双击则闭合,与点一连接flag重置为0if(mode3){if(flag0||flag1)gr.drawLine(x2,y2,x3,y3);if(flag2)gr.drawLine(x1,y1,x2,y2);//双击闭合}关键每画完一段都要x2 x3; y2 y3;将这次画线的起点替换为终点坐标否则下一段会从错误的点出发。五、颜色与线宽颜色按钮设置colordraw()里每次先gr.setColor(color)。线宽文本框输入Integer.parseInt转成整数gr.setStroke(new BasicStroke(w))。parseInt遇到非数字会抛出异常try/catch做出响应返还默认值1。BasicStroke的线宽必须≥1因此加了if (wid 1) wid 1;验证。六、错误记录switch忘了break每个case后不加break就不会终止将会执行其后的所有指令结果不管点哪个按钮mode都变成最后一个值。Graphics没有setStrokesetStroke是Graphics2D的方法。字段声明成Graphics无法调用线宽方法必须声明成Graphics2D。在draw()里改flag导致两个分支都执行flag应该只在事件里改draw()只负责按当前flag画。把改flag写进draw()会出现画完 A 又立刻画 B。七、总结本篇使用Swing完成了一个能画多种图形、能选颜色和线宽的画图工具。介绍了事件处理流程以及绘图部分记录和绘制分离的架构后续添加新图形仅需更改绘制部分即可具有较好的延展性。