用java写一个简单的画图板

2022-06-18 08:05:21

Hello,大家好,这是我的第二篇博客,写的是如何创作一个画图板;因为我以前写的代码都是用dev-C++写的控制台程序,这次学习了JFrame等容器,就想写个画图工具练练手,这一篇我讲的可能会细碎一点。
首先我们要做一个可视化界面,用JFrame类即可,JFrame是一个窗体类,就是我们平时见到的窗体框架,当我们实例化一个JFrame对象时,相当于就是创造了一个窗体,具体的窗体实现我就不在这里详述了,可以看其他博客或者私我。
要做一个画图板,首先要做一个窗体,代码如下:

package Paint_10_28;
import javax.swing.*;
public class Test_Paint {
	public static void main(String[] args) {
		//创建窗体
		JFrame jf =new JFrame();
		//设置窗体名字
		jf.setTitle("画图板");
		//设置窗体大小
		jf.setSize(600,600);
		//设置窗体居中
		jf.setLocationRelativeTo(null);
		//设置窗体可见
		jf.setVisible(true);
		//设置默认关闭形式
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

至此,我们即已经做好了一个简单的窗体程序,接下来我们要在窗体上添加各种功能,比如画画功能,比如重绘功能(后面会详细介绍),比如改变画笔颜色,比如画各种图形,比如清除画板等等。

实现画画功能:
我们要实现画画第一点要想到的是,假设我们要画一条直线,一般是按紧鼠标拖动一段距离,松开鼠标的时候直线便会从起点处画到终点处,我们是不是需要一个鼠标的监听事件呢,对了,就是要监听鼠标的动作,那就需要将JFrame调用addMouselistener,然鹅addMouseListener方法需要一个MouseListener对象作参数,问题又来了,这个MouseListener是一个接口,无法直接实例化对象(详情可参照上一篇博客),这个我们可以新建一个监听的类,在Buttonlistener类中我们实现MouseListener接口,这样子就可以实现对鼠标的监听了(当然要记得重写接口中的方法喔)
第二点要想到的是,如何获取一个画笔来画出图形。在Java中,有一个画笔类,我们只需要实例化画笔对象即可,而画笔对象不是new出来的,我们需要用一个JFrame或者JPanel对象来获取。
形如:JFrame jf = new JFrame();//创建窗体对象
Graphics gf = jf.getGraphics();//通过窗体对象来获得一个画笔
上代码

package Paint_10_28;
import java.awt.event.*;
import javax.swing.JPanel;

public class ButtonListener implements ActionListener,MouseListener,MouseMotionListener {
		private JPanel jp;
		private Graphics gr;
		int x1=0,y1=0,x2=0,y2=0;
		// Override鼠标监听//点击
		public void mouseClicked(MouseEvent e) {
		}
		
		@Override // 按下
		public void mousePressed(MouseEvent e) {
		//鼠标按下的时候,分别获取起点的横纵坐标
			x1=e.getX();
			y1=e.getY();
		}

		@Override // 松开
		public void mouseReleased(MouseEvent e) {
			// TODO Auto-generated method stub
			//鼠标松开的时候,分别获取终点的横纵坐标
			x2 = e.getX();
			y2 = e.getY();
				gr.drawLine(x1, y1, x2, y2);//通过drawLine方法在两个点之间连一条直线(gr是画笔)
		}

		@Override // 进入
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		@Override // 移出
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub

		}

前面我们讲述了如何生成窗体并让鼠标画线,但是我们的画笔一直是现成的,要生成一个画笔我们用如下代码

JPanel jp = new JPanel();
Graphics gf = jp.getGraphics();

有了窗体,鼠标监听和画笔,我们就可以画图了,效果图如下
大家看我已经可以用直线来写字了,当然忽略上面那些按钮,那个是其他功能块,暂时还没讲到
大家刚刚看到我的效果图上面有很多按钮可以控制画不同的线,其实是我添加了很多按钮在面板上面,前面讲过添加MouseListener,如果要用到按钮的话,就要用某个按钮addActionListener,这样子按钮就可以监听到我们的行动,伪代码如下:

JBuuton jb = new JButton("直线");
/*大家看到下面的函数中用了ActionListener的对象,但是这是个接口,不能实例化对象,因此实际的代码中要用他子类的对象才可以,在这里是伪代码就不细说了,当然还涉及到接口方法的重写,这个上一篇博客有写*/
jb.addActionListener(ActionListener e)

有了按钮之后,我们就可以通过按钮来设定不同的线条和画笔颜色,这些多种功能可以自己后期去实现,讲到现在为止,我们的画板已经可以画直线了,实现到这一步,我们发现如果把窗体拉大或者缩小,或者将窗口最小化再最大化画板上的内容会消失

画板的内容消失这不是我们所期待的,我们要怎么处理呢,这里就涉及到窗体的重绘机制,其实我们的窗体在发生大小变化时,会默认自动调用一个函数叫做paint(),如果我们要想自己画的东西不消失,就要重写paint方法,让他在重绘窗体的时候把我们的图形也画进去,那么一个最简单的想法,我们可以建立一个数组,将我们所有绘画的图案存进去然后在调用paint方法的时候,我们除了绘画窗体还要用一个for循环,将数组内部的图案取出来,画在画板上。伪代码如下:

public void paint(Graphics g) {
	//调用super.paint(),因为要先画出窗体,再重绘我们的图案
		super.paint(g);
		len = btl.get_len();
		for(int i=0;i<len;i++) {
		如果数组元素不为空,就可以重绘出图案
			if(ShArr[i]!=null) {
				///重写绘画图像,调用repaint方法
				ShArr[i].repaint(g);
			}
			else {
				break;
			}
		}
	}
//这里是上面调用的repaint方法
public void repaint(Graphics g) {
	//在这里进行
			g.drawLine(x1, y1, x2, y2);
	}

这样子过来,我们就已经基本上实现了
1.构建窗体
2.添加鼠标监听
3.获取画笔画图案
4.添加按钮监听用来控制画板
5.添加重绘机制,让图案不消失

上面的所有代码都是伪代码,合起来并不能实现一个真正的画板,下面上源代码
敲黑板,需要源代码的自取喔,总共三个文件
Shape类,能存放不同的图案

package Paint_10_28;

import java.awt.Graphics;

import javax.swing.JPanel;
//定义一个图案的类,方便对多种图案进行实现和管理
public class Shape{
	//Shape类有name和两点坐标的属性
	private int x1,x2,y1,y2;
	private String name;
	public Shape(int x1,int y1,int x2,int y2,String name){
		this.x1 = x1;
		this.x2 = x2;
		this.y1 = y1;
		this.y2 = y2;
		this.name = name;
	}
	//根据不同的图案名字,开展不同的重绘机制
	public void repaint(Graphics g) {
		switch(name) {
		case "直线":
			g.drawLine(x1, y1, x2, y2);
			break;
		case "矩形":
			g.drawRect(Math.min(x1, x2), Math.min(y1 ,y2), Math.abs(x1-x2), Math.abs(y1-y2));
			break;
		case "圆形":
			g.drawOval(Math.min(x1, x2), Math.min(y1 ,y2), Math.abs(x1-x2), Math.abs(y1-y2));
			break;
		case "曲线":
			g.drawLine(x1, y1, x2, y2);
			break;
		case "多边形":
			g.drawLine(x1, y1, x2, y2);
			break;
		}
	}
}

ButtonListener类,监听类,继承了多个接口,可以实现多种监听

package Paint_10_28;
import java.awt.event.*;

import javax.swing.JButton;
import javax.swing.JPanel;

public class ButtonListener implements ActionListener,MouseListener,MouseMotionListener {
		
		private int index=0;
		private Shape ShArr[];
		private JPanel jp;
		private java.awt.Graphics gr;
		private String zhiling="";
		int x0=0,y0=0,x1=0,y1=0,x2=0,y2=0,x3=0,x4=0,y3=0,y4=0,start_x=0,start_y=0;
		public void set_jp(JPanel jp) {
			this.jp=jp;
			}
		public void set_gr(java.awt.Graphics G) {
			this.gr = G;
		}
		public void set_ShArr(Shape ShArr[]) {
			this.ShArr = ShArr;
		}
		public int get_len() {
			return index;
		}
		public void actionPerformed(ActionEvent e) 
		{
			// TODO Auto-generated method stub
			x1 = 200; y1 = 200;
			if (e.getActionCommand()=="") {
				JButton j = (JButton)e.getSource();
				gr.setColor(j.getBackground());
			}
			else {
				zhiling = e.getActionCommand();
				if("清除".equals(zhiling)) {
					//System.out.println("这里是buttonlistener的清除");
					index = 0;
				//	p.set_len(len);
					jp.repaint();
					x4 = 0;
					y4 = 0;
				}
			}
		}
		//鼠标拖动时间,用来绘画曲线
		public void mouseDragged(MouseEvent e) {
			//System.out.println("Drag");
			if("曲线".equals(zhiling)) {
				x1 = x0; y1 = y0; x0 = e.getX(); y0 = e.getY();
				gr.drawLine(x1,y1,x0,y0);
				ShArr[index++] = new Shape(x1,y1,x0,y0,zhiling);
				
			}
		}

		// Override鼠标监听//点击鼠标点击时间,用来绘画多边形
		public void mouseClicked(MouseEvent e) {
			// TODO Auto-generated method stub
			if("多边形".equals(zhiling)) {
				
				if(x4==0&&y4==0){
					
					x4 = e.getX(); y4 = e.getY();
					start_x = x4; start_y = y4;
					//gr.drawLine(x3, y3, x4, y4);
				}
				else {
					//System.out.println("Here is duobianxing ");
					x3 = x4; y3 = y4; x4 = e.getX(); y4 = e.getY();
					gr.drawLine(x3, y3, x4, y4);
					ShArr[index++] = new Shape(x3,y3,x4,y4,zhiling);
				}
				if(e.getClickCount()==2){
				//System.out.println("双击");
				//System.out.println("双击");
				x4 =0; y4=0;
				gr.drawLine(start_x, start_y, e.getX(), e.getY());
				ShArr[index++] = new Shape(start_x,start_y,e.getX(),e.getY(),zhiling);
			}	
			}
			
		}

		//当鼠标按下的时候,获取起点的坐标
		@Override // 按下
		public void mousePressed(MouseEvent e) {
		//	System.out.println("按下");
			
			if("曲线".equals(zhiling)){
				x0 = e.getX();
				y0 = e.getY();
			}
			// TODO Auto-generated method stub
			x1 = e.getX();
			y1 = e.getY();
			
		}
		//当鼠标松开的时候,获取终点的坐标
		@Override // 松开
		public void mouseReleased(MouseEvent e) {
			// TODO Auto-generated method stub
			x2 = e.getX();
			y2 = e.getY();
		//	System.out.println("松开");
			if("直线".equals(zhiling)){
				gr.drawLine(x1, y1, x2, y2);
				ShArr[index++] = new Shape(x1,y1,x2,y2,zhiling);
				//System.out.println("长度是 "+len);
			}
			else if("矩形".equals(zhiling)) {
				gr.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1-x2), Math.abs(y1-y2));
				ShArr[index++] = new Shape(x1,y1,x2,y2,zhiling);
			}
			else if("圆形".equals(zhiling)) {
				gr.drawOval(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1-x2), Math.abs(y1-y2));
				ShArr[index++] = new Shape(x1,y1,x2,y2,zhiling);
			}
		}
		//剩下的方法,可以暂时不用管
		@Override // 进入
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		@Override // 移出
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub

		}

		public void mouseMoved(MouseEvent e) {
			
		}
	}

paint类,主函数所在的类,里面实例化了窗体对象,重写了paint方法

package Paint_10_28;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Paint extends JPanel{
	//建立一个很大的数组,用来存放图形元素
	private Shape[] ShArr = new Shape[10000];
	public static void main(String[] args) {
		Paint p = new Paint();
	}

	JFrame jf = new JFrame();
	//JPanel jp = new JPanel();
	int len = 0;
	//实例化一个ButtonListener对象,实现了多种接口
	ButtonListener btl = new ButtonListener();
	public Paint() {
		//实现一个窗体
		jf.setTitle("画图板");
		jf.setLocation(450, 100);
		jf.setSize(700, 600);
		this.setPreferredSize(new Dimension(600, 500));
		//声明两个数组,包含各种指令
		String[] command = { "开始", "清除", "直线", "曲线", "多边形", "矩形","圆形" };
		Color[] color = { Color.BLACK, Color.BLUE, Color.YELLOW, Color.RED, Color.GREEN };
		//设置布局为流式布局
		jf.setLayout(new FlowLayout());
		
		/*在下面的两个循环中将各种按钮添加进入动作监听器中,其中addActionListener参数为btl,
		 btl是一个Buttonlistener的对象
		 */
		for (int i = 0; i < command.length; i++) {
			JButton jb = new JButton(command[i]);
			jb.addActionListener(btl);
			jf.add(jb);
		}
		for (int i = color.length - 1; i >= 0; i--) {
			JButton jb = new JButton();
			jb.setBackground(color[i]);//设置背景颜色
			Dimension dm = new Dimension(20, 20);//设置大小
			jb.setPreferredSize(dm);
			jb.addActionListener(btl);
			jf.add(jb);
		}
		//将JPanel对象添加进入jf窗体对象中,让他生效
		jf.add(this);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);	//设置可见
		Graphics gf = this.getGraphics();	//获取画笔,我们的this代表当前类的对象,正好是一个JPanel的对象
		this.addMouseListener(btl);		//添加鼠标监听器,用于画图
		this.addMouseMotionListener(btl);	//添加鼠标模式监听器,用于绘画曲线
		btl.set_gr(gf);		//设置另外一个类的画笔
		btl.set_jp(this);	//设置另外一个类的JPanel容器
		btl.set_ShArr(ShArr);	//设置另外一个类的数组
	}
	public void paint(Graphics g) {
		super.paint(g);		//调用父类的paint方法,用来画出窗体
		len = btl.get_len();	//获取数组的长度
		//重绘我们的图案
		for(int i=0;i<len;i++) {
			if(ShArr[i]!=null) {
				//重写绘画图像,但是我只重绘了图形,忘记加颜色了;
				ShArr[i].repaint(g);
			}
			else {
				break;
			}
		}
	}
}

这三个类是画图板的主要组成部分,需要的同学可以自取。画图板中间许多比较复杂的功能我并没有解释的很清楚,只是给出了一个大体的思路,对源代码有疑问的可以mail【1124397151@qq.com】这个画图板搞了我三天时间,通过这画图板我对各种监听和继承有了更深的理解,另外一点重要的理解是,Java中不同的类之间不方便共享数据,但是他可以共享类的对象,因此我们要共享数据可以通过将某个类的对象当做参数传递过去,然后通过getxxx()方法来获取那个类中的某个数据,在这个画图板中,图案数组的长度我就是这样处理的。
最后效果图如下
在这里插入图片描述
/写的比较糙,有什么疑问的可以发邮件给我,欢迎大家批评指正***/

  • 作者:hnu_Cheng
  • 原文链接:https://blog.csdn.net/qq_36659438/article/details/83629120
    更新时间:2022-06-18 08:05:21