用 Java Swing 手写一个简易画图工具(附代码详解)

张开发
2026/6/23 0:05:05 15 分钟阅读
用 Java Swing 手写一个简易画图工具(附代码详解)
前言适合新手小白的 Java GUI 小项目带你从零实现直线、矩形、椭圆、三角形、曲线、橡皮擦、画笔调节功能理解事件监听与绘图原理。基础的语法在我的第一第二篇博客里有详细的介绍。项目简介DrawUI——负责界面搭建DrawListener —— 负责监听鼠标和按钮事件完成绘图逻辑运行效果是一个 900×900 的窗口顶部有 11 个功能按钮中间是白色画布。你可以画画直线、矩形、椭圆、等腰三角形、任意三角形、多边形、曲线使用橡皮擦擦除切换红色 / 黑色画笔加粗 / 变细画笔下面我们就一步步拆解这个画图工具。一、界面搭建 —— DrawUI 类DrawUI 负责创建窗口、面板和按钮并将它们组装起来。1.1 说明BorderLayout边界布局JFrame 的内容面板默认使用边界布局。它把窗口分成五个区域NORTH北/上、SOUTH南/下、EAST东/右、WEST西/左、CENTER中间。没有说明则默认放到中间。每个区域只能放一个组件如果你不往 Center 区域放任何容器而是直接放多个按钮那么只会显示最后添加的那个。要想在一个区域放多个按钮可以先把按钮放到 JPanel 轻量级容器里再把 JPanel 放到该区域。JPanel 轻量级容器默认使用FlowLayout流式布局组件从左到右依次排列。设置监听器监听器接口监听对象主要功能ActionListener按钮切换绘图模式、颜色、粗细MouseListener绘图面板捕捉按下/释放/点击绘制基础图形MouseMotionListener绘图面板捕捉拖动实现曲线和橡皮擦监听器决定“在哪儿操作”给合适的对象设置监听器如鼠标移动监听器设置给绘图面板这样当鼠标移动画笔移动到顶部按钮面板时就不会显示出笔记盖住按钮。获取画笔画笔决定“画在哪儿”getGraphics() 方法获取画笔从绘图界面获取所以必须要在绘图界面显示后操作否则会返回null1.2 代码importjava.awt.Color;importjava.awt.Component;importjava.awt.Dimension;importjava.awt.Graphics;importjavax.swing.JButton;importjavax.swing.JFrame;importjavax.swing.JPanel;publicclassDrawUI{publicvoidinitUI(){//设置窗体JFramejfnewJFrame();jf.setSize(900,900);jf.setTitle(画图工具);jf.setDefaultCloseOperation(3);jf.setLocationRelativeTo(null);// 顶部按钮面板JPanelnorthPanelnewJPanel();//设置容器northPanel.setBackground(Color.pink);//容器的背景颜色便于区分northPanel.setPreferredSize(newDimension(0,40));//容器的尺寸宽度高度//这个方法设置的是理想大小宽度为0是因为BorderLayout会自动将组件宽度拉伸到与窗口同宽高度则尊重你设置的高度。jf.add(northPanel,BorderLayout.NORTH);//将容器添加到窗口上方// 绘图面板JPaneldrawPanelnewJPanel();drawPanel.setBackground(Color.white);jf.add(drawPanel);//未说明位置默认中心// 创建监听器对象//DrawListener是自定义的类里面实现了监听器的接口DrawListenerlistenernewDrawListener();drawPanel.addMouseListener(listener);//添加鼠标监听器drawPanel.addMouseMotionListener(listener);//添加鼠标移动监听器// 添加功能按钮String[]namenewString[]{直线,矩形,椭圆,等腰三角形,任意三角形,曲线,橡皮擦,红色,黑色,画笔变粗,画笔变细};for(inti0;iname.length;i){JButtonjbunewJButton(name[i]);northPanel.add(jbu);jbu.addActionListener(listener);}jf.setVisible(true);// 从画板获取 Graphics 对象传给监听器画笔显示一定要在面板显示可见之后GraphicsgdrawPanel.getGraphics();//从绘图界面获取画笔listener.grg;//给自定义类里面的画笔赋值即传递画笔}publicstaticvoidmain(String[]args){DrawUIuinewDrawUI();ui.initUI();}}二、核心逻辑 —— DrawListener 类2.1说明这个类实现了三个接口publicclassDrawListenerimplementsMouseListener,ActionListener,MouseMotionListener这意味着它必须重写这些接口里的所有方法。设置画笔粗细Graphics2D能设置画笔粗细、变为虚线等转换后可以用setStroke() 方法安装新笔头BasicStroke 是 Java 提供的一个笔头定义类可以改变笔头粗细等这里只设置粗细别的用默认值。常规BasicStrokethickStrokenewBasicStroke(w);//w为设置的具体宽度g2.setStroke(thickStroke);简单写法g2.setStroke(newBasicStroke(w));画矩形、椭圆drawRect(x, y, width, height); x、y是初始位置后边跟着宽度和高度这些是初始位置相加的而我们不一定是从左上角画到右下角所以我们要设置x为按下和松开时最小的位置值。椭圆同理不过是矩形里的内切椭圆罢了。等腰三角形数学思路在代码里这里只能画底和高相等的等腰三角形多边形这里的操作是先画一条边下一次点击时连接松开时的位置和这个点再下一次连接两点直到连击两次连接第一条边和该位置画曲线、橡皮擦鼠标移动监听器里的拖动方法你按住鼠标左键在画板上拖动的时候这个方法会被疯狂调用每秒几十次所以曲线其实时不断连接两条极短的线段。橡皮擦同理本质是用白色覆盖2.2代码importjava.awt.*;importjava.awt.event.*;publicclassDrawListenerimplementsMouseListener,ActionListener,MouseMotionListener{publicGraphicsgr;//设置传递过来的画笔对象publicintx1,y1,x2,y2,x3,y3;//定义全局变量方便每个作用域publicStringname;//设置字符串获取按钮文字publicintflag1;//设置标记位控制代码执行顺序publicintw1;//设置画笔粗细初始值//操作按钮的动作监听器获取按钮文字以执行相应功能publicvoidactionPerformed(ActionEvente){//获取按钮文字namee.getActionCommand();//获取按钮文字System.out.println(点击按钮name name);if(红色.equals(name)){gr.setColor(Color.red);}if(黑色.equals(name)){gr.setColor(Color.black);}if(画笔变粗.equals(name)){w;Graphics2Dg2(Graphics2D)gr;//画笔变为2D(Graphics2D)gr 是强制类型转换g2.setStroke(newBasicStroke(w));}if(画笔变细.equals(name)){w--;Graphics2Dg2(Graphics2D)gr;//画笔变为2Dg2.setStroke(newBasicStroke(w));}}//这里点击方法操作的是任意三角形图形和任意多边形的第二步//确定任意三角形的顶点位置和任意多边形的拐点位置publicvoidmouseClicked(MouseEvente){//点击是原位按下和松开x3e.getX();y3e.getY();if(任意三角形.equals(name)flag2){gr.drawLine(x1,y1,x3,y3);gr.drawLine(x2,y2,x3,y3);System.out.println(x3 x3 y3 y3);flag1;//变回去让下一次画图正常}if(多边形.equals(name)flag3){gr.drawLine(x2,y2,x3,y3);x2x3;y2y3;//flag没变继续执行一次点击时的效果if(e.getClickCount()2){//点击数为两次gr.drawLine(x1,y1,x3,y3);flag1;//变回去}}}//这里操作的是所有图形的第一步publicvoidmousePressed(MouseEvente)//按下{System.out.println(按下);//获取当前坐标值if(flag1){x1e.getX();y1e.getY();}//记录下来便于调试System.out.println(x1 x1 y1 y1);System.out.println(flagflag);}publicvoidmouseReleased(MouseEvente)//释放从按压到释放有位置移动{System.out.println(松开);if(flag1){x2e.getX();y2e.getY();}System.out.println(x2 x2 y2 y2);if(直线.equals(name)){//绘制图形gr.drawLine(x1,y1,x2,y2);}if(矩形.equals(name)){intwidthMath.abs(x2-x1);intheightMath.abs(y2-y1);intxMath.min(x1,x2);intyMath.min(y1,y2);gr.drawRect(x,y,width,height);}if(椭圆.equals(name)){intwidthMath.abs(x2-x1);//abs是绝对值函数intheightMath.abs(y2-y1);intxMath.min(x1,x2);intyMath.min(y1,y2);gr.drawOval(x,y,width,height);}if(等腰三角形.equals(name)){intmidX(x1x2)/2;intmidY(y1y2)/2;intdxx2-x1;intdyy2-y1;// 顶点坐标这里取垂直向量 (-dy, dx) 加到中点可调整符号intx4midX-dy;inty4midYdx;// 画底边gr.drawLine(x1,y1,x2,y2);// 画两条腰gr.drawLine(x1,y1,x4,y4);gr.drawLine(x2,y2,x4,y4);}if(任意三角形.equals(name)){//先画底边gr.drawLine(x1,y1,x2,y2);flag2;//执行上面的第二步}if(多边形.equals(name)flag1){gr.drawLine(x1,y1,x2,y2);flag3;}System.out.println(flagflag);}//鼠标拖动publicvoidmouseDragged(MouseEvente){intxe.getX();intye.getY();if(曲线.equals(name)){gr.drawLine(x1,y1,x,y);x1x;y1y;//更新起点}if(橡皮擦.equals(name)){//设置粗细Graphics2Dg2(Graphics2D)gr;//画笔变为2Dg2.setStroke(newBasicStroke(w));gr.setColor(Color.white);gr.drawLine(x1,y1,x,y);x1x;y1y;gr.setColor(Color.BLACK);//变黑色是在变白色之后所以画的时候时白色画完才是黑色g2.setStroke(newBasicStroke(1));}}//虽然没有用到但也要重写publicvoidmouseMoved(MouseEvente){}publicvoidmouseEntered(MouseEvente)//鼠标光标进入{}publicvoidmouseExited(MouseEvente)//鼠标光标离开{}}三、不足之处这个画图工具很适合新手练习但也存在一些明显不足没有保存功能只能画空心图形没有清除画板功能四、总结通过这个项目你学会了✅ 如何用 Swing 搭建一个简单的 GUI 程序✅ 如何同时实现多个监听器接口✅ 鼠标事件按下、释放、点击、拖动与绘图 API 的配合✅ 利用 flag 变量管理多步骤绘图状态

更多文章