JAVA画图程序开发
PS.2025.9.30已进行更新,修复画“任意三角形”“任意多边形”时不能连续画多个的问题。
所需库
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
大致思想
创建一个有各种功能按钮的窗口,实现分别点击按钮有不同功能的呈现。
按钮方面:设置动作监听器
画图方面:设置鼠标监听器,设置一些形状的绘图操作
一,创建绘图窗体
通过JFrame创建一个窗口,并设计该窗口的大小,背景色,布局管理器(我们将使用JPanel布局管理器,jframe默认管理器是Jpanel,因此可以不进行设置)。
JPanel布局管理器
下图为JPanel布局管理器的布局,该管理器可以将一个窗体分成不同部分,以免相互打扰。我们的画图窗口设计就是让South区域作为我们按钮的区域,让Center区域作为我们的画布。

public void initUI(){
//创建窗口
JFrame jf = new JFrame();
//窗口大小
jf.setSize(900,900);
//窗口标题
jf.setTitle("画图工具");
jf.setLocationRelativeTo(null); //居中显示
jf.setDefaultCloseOperation(3); //退出进程
//创建各个区域用来存放组件
JPanel jp = new JPanel();
//设置背景颜色
jp.setBackground(Color.gray);
//设置高度
jp.setPreferredSize(new Dimension(0,50));
//将刚刚创建的jp对象绑定在jf窗口上
jf.add(jp,BorderLayout.NORTH);
//以下同理,创建画布区域
JPanel jp2 = new JPanel();
jp2.setBackground(Color.white);
jf.add(jp2,BorderLayout.CENTER);
jf.setVisible(true);
// 获取窗口的绘图对象,注意该代码必须写在“显示可见”的代码之后
Graphics g = jf.getGraphics();
二,先实现接口,重写抽象方法
创建一个新的类,实现接口,并重写抽象方法,该步骤我们分别实现以下三个接口MouseListener,ActionListener,MouseMotionListener
(请注意在接口中我们需要写上全部抽象方法,不能遗漏。我们可以只写重写我们需要的方法)
public class Drawlistener implements MouseListener , ActionListener, MouseMotionListener {
//动作监听器(ActionListener),设置按钮相关操作
public void actionPerformed(ActionEvent e){};
//鼠标监听器(MouseListener),设置绘画相关操作
public void mouseClicked(MouseEvent e){};
public void mousePressed(MouseEvent e){};
public void mouseReleased(MouseEvent e){};
public void mouseEntered(MouseEvent e){};
public void mouseExited(MouseEvent e){};
//鼠标运动监听器(MouseMotionListener),"曲线"的设置需要其支持
public void mouseDragged(MouseEvent e){};
public void mouseMoved(MouseEvent e){};
}
之后我们将重写其中我们需要的方法的内容。
二,添加按钮与动作监听器,实现绘图操作
我们需要创建"直线",“曲线”,“等腰三角形”,“矩形”,“任意三角形”,“任意多边形”,“红”,“黄”,“蓝”,“粗”,“细”,"橡皮擦"这些按钮。
//创建按钮
JButton jbu = new JButton("直线");
jf.add(jbu);
JButton jbu2 = new JButton("等腰三角形");
jf.add(jbu2);
JButton jbu3 = new JButton("矩形");
jf.add(jbu3);
JButton jbu4 = new JButton("任意三角形");
//为按钮设置监听器
jbu.addActionListener(listener);
jbu2.addActionListener(listener);
jbu3.addActionListener(listener);
jbu4.addActionListener(listener);
我们可以像上面那样一个一个设置但这样会显得重复与浪费时间。我们可以通过一个循环便捷的实现以上操作,若有新的按钮需要添加也可直接写在数组中。
//创建一个数组储存按钮
String[] function={"直线","曲线","等腰三角形","矩形","任意三角形","任意多边形","红","黄","蓝","粗","细","橡皮擦"};
//循环创建
for(int i=0;i<=function.length-1;i++){
//依次提取数组中的每一个值
String jbutton=function[i];
//创建按钮对象,并命名为jbutton变量
JButton jbu = new JButton(jbutton);
jf.add(jbu);
//设置按钮监听器
jbu.addActionListener(listener);
按钮创建完成我们就需要添加对应操作。首先在Drawlistener里创建一个“gr”绘图对象,再从initUI()窗口中接收窗口的对象,并为画布区域添加鼠标监听器,鼠标运动监听器(实现画曲线的操作)。之后我们就将在监听器里去设置各种功能。
public void initUI(){
//获取到的Graphics对象g可以用于在jp2组件上进行绘图操作
Graphics g = jp2.getGraphics();
//给jp2区域设置监听器
jp2.addMouseListener(listener);
jp2.addMouseMotionListener(listener);
//gr接收g
listener.gr = g;
}
public class Drawlistener implements MouseListener , ActionListener, MouseMotionListener {
public Graphics gr;
}
我们通过读取按钮的文字来对按钮进行区分,并一一设置功能。这里为什么采用if-else语句并通过两个变量来获取按钮名称,是因为我们需要在选择颜色时不会影响画线操作的正常进行。(如果我们不进行该操作,选完颜色后我们还得再次点击相关画线按钮。)选择线段的粗细的逻辑与选择颜色是一致的。这里比较难理解,可以自行进行测试看看没有if-else语句的效果。
public class Drawlistener implements MouseListener , ActionListener, MouseMotionListener {
public Graphics gr;
//定义变量获取按钮文字
String colorName;
String penType;
//先在按钮动作监听器里设置
public void actionPerformed(ActionEvent e){
//将gr强制转换为Graphics2D,这样才能修改画笔的粗细
Graphics2D g2=(Graphics2D)gr;
//获取按钮文字
colorName=e.getActionCommand();
//判断按钮,对按钮进行区分
//判断颜色按钮
if("红".equals(colorName)||"黄".equals(colorName)||"蓝".equals(colorName)||"橡皮擦".equals(colorName)) {
switch (colorName) {
case "红":
//设置画笔颜色
gr.setColor(Color.red);
break;
case "黄":
gr.setColor(Color.yellow);
break;
case "蓝":
gr.setColor(Color.BLUE);
break;
}
}else{
//用penType去接收非颜色的值,并单独存储,不影响画图,选粗细的进行
penType=e.getActionCommand();
}
}
//判断粗细按钮,原理与上相同
if ("粗".equals(penType)||"细".equals(penType)){
//设置“粗”该有多粗,“细”该有多细
BasicStroke bsThick =new BasicStroke(6);
BasicStroke bsFine =new BasicStroke(1);
switch (penType){
case "粗":{
g2.setStroke(bsThick);
}break;
case "细":{
g2.setStroke(bsFine);
}break;
}else {
//用penType去接收非画笔粗细的值,并单独存储,不影响画图的进行
name=penType;
}
}
}
实现画图操作
我们在画图过程中发现,画一条直线的本质就是在最开始点击鼠标左键的位置位置获取一次坐标,在松开鼠标的位置再次获取坐标并将两个坐标连线,实现一条直线的画图。
public class Drawlistener implements MouseListener , ActionListener, MouseMotionListener {
public void mousePressed(MouseEvent e){
System.out.println("按下");
//获得初始的位置
x1 = e.getX();
y1 = e.getY();
System.out.println(x1 + ":" + y1);
}
public void mouseReleased(MouseEvent e){
if(flag==1) {
System.out.println("松开");
//获得结束的位置
x2 = e.getX();
y2 = e.getY();
}
}
接下来我们可以进一步实现其它图形的设计,我们先观察鼠标在画图时会进行的相关操作。先是调用按下方法,再是松开方法。
我们便可以通过这个设计各种图案,不过其中有两个图形比较特殊,“矩形”“任意三角形”“任意多边形”“曲线”“橡皮擦”等。
任意三角形的设计,我们可以先设计一条直线,再点击任意位置连接此点与其它两个端点。但实际操作下来我们会发现当我们点击时,会再一次进行按下松开的操作,导致三点重合。因此我们要在画出一条直线后,不再调用按下松开的方法。给两个方法一个判断条件(flag==1),在按下“任意三角形”按钮并画一条直线后使flag的值加一,使此时flag=2,从而跳过这两步。
矩形的设计,我们可以直接用四条线连接,但这样运行效率不高。采用drawRect()方法能直接实现画矩形,不过这样也会出现问题如拉取这个矩形是从右下角拉到左上角时图形会错位,使矩形左上角顶点不在鼠标松开的位置。这是因为画布的坐标有大小之分,drawRect()实际上就是通过计算坐标之间差值来确定位置。通过思考我们能得知该矩形的左上角无论x,y值都是最小的,我们调用Math下的“绝对值”,“最小值”方法来进行设计。
任意多边形的设计,与实现任意三角形很类似,也是通过画出一条直线后不断点击不同位置连线,最后首位相连。不同的是我们需要点击后每条线段首位相连,可通过赋值的方法使得上一步的“头”变成下一步的尾。
曲线的设计,我们会在mouseDragged()方法内实现,与任意多边形的不断连线相似,我们也是不断的把“头”赋值给“尾”来实现。
橡皮擦的设计,本质就是用与背景色相同的画笔来涂色。与曲线设计相似,不过我们要通过Graphics2D来改变笔的粗细。
以下是代码的呈现。
public void mouseClicked(MouseEvent e){
// System.out.println("点击");
x3 = e.getX();
y3 = e.getY();
if("任意三角形".equals(name)){
System.out.println("点击");
gr.drawLine(x2,y2,x3,y3);
gr.drawLine(x1,y1,x3,y3);
//重新设置flag值,使得能重新画线
flag=1;
}
if("任意多边形".equals(name)){
System.out.println("点击");
gr.drawLine(x2, y2, x3, y3);
x2=x3;
y2=y3;
//双击后,首位相连
if(e.getClickCount()==2){
gr.drawLine(x1,y1,x4,y4);
//重新设置flag值,使得能重新画线
flag=1;
}
}
}
public void mousePressed(MouseEvent e){
if(flag==1) {
System.out.println("按下");
//获取坐标
x1 = e.getX();
y1 = e.getY();
System.out.println(x1 + ":" + y1);
}
}
public void mouseReleased(MouseEvent e){
if(flag==1) {
System.out.println("松开");
x2 = e.getX();
y2 = e.getY();
}
switch (name){
case "等腰三角形":{
//恢复初始flag值,以便其它画图的正常进行
flag=1;
int halfx = (x1+x2)/2;
gr.drawLine(halfx,y1,x1,y2);
gr.drawLine(x1,y2,x2,y2);
gr.drawLine(halfx,y1,x2,y2);
}break;
case "直线":
flag=1;
gr.drawLine(x1, y1, x2, y2);
break;
case "矩形":{
int width=Math.abs(x2-x1);
int height=Math.abs(y2-y1);
flag=1;
int miniX =Math.min(x1,x2);
int miniY =Math.min(y1,y2);
gr.drawRect(miniX,miniY,width,height);
}break;
//这两个图形的首步画法相同
case "任意三角形", "任意多边形":{
if(flag==1) {
gr.drawLine(x1, y1, x2, y2);
//flag值加1,避免再次触发“按下”“松开”方法
flag++;
}
}break;
}
}
public void mouseDragged(MouseEvent e){
if("曲线".equals(name)){
x5=e.getX();
y5=e.getY();
System.out.println("拖拽");
gr.drawLine(x1, y1, x5, y5);
x1=x5;
y1=y5;
}
if ("橡皮擦".equals(name)){
//强制转换gr的数据类型为Graphics2D,以实现调整画笔的粗细
Graphics2D g2=(Graphics2D)gr;
x5=e.getX();
y5=e.getY();
//设置画笔颜色为白色,实现擦除功能
gr.setColor(Color.white);
//通过对象g2,调整画笔大小
g2.setStroke(new BasicStroke(8));
gr.drawLine(x1,y1,x5,y5);
x1=x5;
y1=y5;
}
}
解决选择橡皮擦后,画笔的粗细颜色改变的问题
当我们选择橡皮擦后,画笔gr的设置就变成了橡皮擦画笔的设置。我们需要一种方法把画笔的设置变回原来的。我们可以先给画笔设置一个默认值,并在选择颜色与粗细后保存相关数据再次调用。这样,用完橡皮擦后又能便利的画图了。
//创建颜色与粗细的初始值
Color saveColor=Color.BLACK;
float saveStroke = 1;
public void actionPerformed(ActionEvent e){
//将gr强制转换为Graphics2D,这样才能修改画笔的粗细
Graphics2D g2=(Graphics2D)gr;
//获取按钮文字
colorName=e.getActionCommand();
//判断颜色按钮
if("红".equals(colorName)||"黄".equals(colorName)||"蓝".equals(colorName)) {
switch (colorName) {
case "红":
gr.setColor(Color.red);
break;
case "黄":
gr.setColor(Color.yellow);
break;
case "蓝":
gr.setColor(Color.BLUE);
break;
}
//这里保存一下我们选择的颜色选项
saveColor=gr.getColor();
}else{
penType=e.getActionCommand();
}
if ("粗".equals(penType)||"细".equals(penType)){
BasicStroke bsThick =new BasicStroke(6);
BasicStroke bsFine =new BasicStroke(1);
switch (penType){
case "粗":{
g2.setStroke(bsThick);
}break;
case "细":{
g2.setStroke(bsFine);
}break;
}
//通过判断我们选择的选项来保存粗细
if("粗".equals(penType)){
saveStroke=6;
}else if("细".equals(penType)){
saveStroke=1;
}
}else {
name=penType;
//在未进行颜色与画笔粗细时,调用保存的画笔颜色与粗细
gr.setColor(saveColor);
g2.setStroke(new BasicStroke(saveStroke));
}
}
最终代码呈现
DrawUI.java文件
import javax.swing.*;
import java.awt.*;
public class DrawUI {
public void initUI(){
//创建窗口
JFrame jf = new JFrame();
//窗口大小
jf.setSize(900,900);
//窗口标题
jf.setTitle("画图工具");
jf.setLocationRelativeTo(null); //居中显示
jf.setDefaultCloseOperation(3); //退出进程
//创建各个区域用来存放组件
JPanel jp = new JPanel();
//设置背景颜色
jp.setBackground(Color.gray);
//设置高度
jp.setPreferredSize(new Dimension(0,50));
//将刚刚创建的jp对象绑定在jf窗口上
jf.add(jp,BorderLayout.NORTH);
//以下同理,创建画布区域
JPanel jp2 = new JPanel();
jp2.setBackground(Color.white);
jf.add(jp2,BorderLayout.CENTER);
//创建一个数组保存按钮名称
String[] function={"直线","曲线","等腰三角形","矩形","任意三角形","任意多边形","红","黄","蓝","粗","细","橡皮擦"};
Drawlistener listener=new Drawlistener();
for(int i=0;i<=function.length-1;i++){
//依次提取数组中的每一个值
String jbutton=function[i];
//创建按钮对象,并命名为jbutton变量
JButton jbu = new JButton(jbutton);
jp.add(jbu);
//设置按钮监听器
jbu.addActionListener(listener);
}
jf.setVisible(true);
//获取到的Graphics对象g可以用于在jp2组件上进行绘图操作
Graphics g = jp2.getGraphics();
//给jp2区域设置监听器
jp2.addMouseListener(listener);
jp2.addMouseMotionListener(listener);
listener.gr = g;
}
public static void main(String[] args) {
DrawUI ui = new DrawUI();
ui.initUI();
}
}
Drawlistener.java文件
import java.awt.*;
import java.awt.event.*;
public class Drawlistener implements MouseListener , ActionListener, MouseMotionListener {
public Graphics gr;
//定义变量获取按钮文字
String name;
String penType;
String colorName;
public int flag=1;
public int x1,y1,x2,y2,x3,y3,x4,y4,x5,y5;
//创建颜色与粗细的初始值
Color saveColor=Color.BLACK;
float saveStroke = 1;
public void actionPerformed(ActionEvent e){
//将gr强制转换为Graphics2D,这样才能修改画笔的粗细
Graphics2D g2=(Graphics2D)gr;
//获取按钮文字
colorName=e.getActionCommand();
//判断颜色按钮
if("红".equals(colorName)||"黄".equals(colorName)||"蓝".equals(colorName)) {
switch (colorName) {
case "红":
gr.setColor(Color.red);
break;
case "黄":
gr.setColor(Color.yellow);
break;
case "蓝":
gr.setColor(Color.BLUE);
break;
}
//这里保存一下我们选择的颜色选项
saveColor=gr.getColor();
}else{
////用penType去接收非颜色的值,并单独存储,不影响画图,选粗细的进行
penType=e.getActionCommand();
}
//判断粗细按钮
if ("粗".equals(penType)||"细".equals(penType)){
BasicStroke bsThick =new BasicStroke(6);
BasicStroke bsFine =new BasicStroke(1);
switch (penType){
case "粗":{
g2.setStroke(bsThick);
}break;
case "细":{
g2.setStroke(bsFine);
}break;
}
//通过判断我们选择的选项来保存粗细
if("粗".equals(penType)){
saveStroke=6;
}else if("细".equals(penType)){
saveStroke=1;
}
}else {
//用penType去接收非画笔粗细的值,并单独存储,不影响画图的进行
name=penType;
//在未进行颜色与画笔粗细时,调用保存的画笔颜色与粗细
gr.setColor(saveColor);
g2.setStroke(new BasicStroke(saveStroke));
}
}
public void mouseClicked(MouseEvent e){
// System.out.println("点击");
x3 = e.getX();
y3 = e.getY();
x4 = e.getX();
y4 = e.getY();
if("任意三角形".equals(name)){
System.out.println("点击");
gr.drawLine(x2,y2,x3,y3);
gr.drawLine(x1,y1,x3,y3);
}
if("任意多边形".equals(name)){
System.out.println("点击");
gr.drawLine(x2, y2, x4, y4);
x2=x4;
y2=y4;
//双击后,首位相连
if(e.getClickCount()==2){
gr.drawLine(x1,y1,x4,y4);
}
}
}
public void mousePressed(MouseEvent e){
if(flag==1) {
System.out.println("按下");
//获取坐标
x1 = e.getX();
y1 = e.getY();
System.out.println(x1 + ":" + y1);
}
}
public void mouseReleased(MouseEvent e){
if(flag==1) {
System.out.println("松开");
x2 = e.getX();
y2 = e.getY();
}
switch (name){
case "等腰三角形":{
//恢复初始flag值,以便其它画图的正常进行
flag=1;
int halfx = (x1+x2)/2;
gr.drawLine(halfx,y1,x1,y2);
gr.drawLine(x1,y2,x2,y2);
gr.drawLine(halfx,y1,x2,y2);
}break;
case "直线":
flag=1;
gr.drawLine(x1, y1, x2, y2);
break;
case "矩形":{
int width=Math.abs(x2-x1);
int height=Math.abs(y2-y1);
flag=1;
int miniX =Math.min(x1,x2);
int miniY =Math.min(y1,y2);
gr.drawRect(miniX,miniY,width,height);
}break;
//这两个图形的首步画法相同
case "任意三角形", "任意多边形":{
if(flag==1) {
gr.drawLine(x1, y1, x2, y2);
//flag值加1,避免再次触发“按下”“松开”方法
flag++;
}
}break;
}
}
public void mouseDragged(MouseEvent e){
if("曲线".equals(name)){
x5=e.getX();
y5=e.getY();
System.out.println("拖拽");
gr.drawLine(x1, y1, x5, y5);
x1=x5;
y1=y5;
}
if ("橡皮擦".equals(name)){
Graphics2D g2=(Graphics2D)gr;
x5=e.getX();
y5=e.getY();
//设置画笔颜色为白色,实现擦除功能
gr.setColor(Color.white);
//通过对象g2,调整画笔大小
g2.setStroke(new BasicStroke(8));
gr.drawLine(x1,y1,x5,y5);
x1=x5;
y1=y5;
}
}
//我们不需要使用以下方法,但因为需要实现接口,必须将他们写出。
public void mouseMoved(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
}
489

被折叠的 条评论
为什么被折叠?



