画图板编写总结及心得

      关于画图板程序的编写实践...实在是个让我纠结到蛋疼的事,我从一开始就跟熊哥教的方法不一致,而这是由于之前翘课,左哥给我补课的时候,又很水的只讲了一小部分就让我们去做...其结果就是,我写这个程序的思路和结构跟熊哥他们的完全不一致...以至于...我的代码分了4个类,写了足足600多行代码,还不算注解...实现功能的时候也就又各种报错......庆幸的是终于做完了,这个坑爹的程序,废话不说了,我接下来说说画图板程序的具体的一些东西,鉴于我的代码个各种混乱以至于我自己都看不懂,我就拿“标准版”的思路和代码来说说:

 

       首先是思路和结构,因为是窗口的程序,所以根据窗口上的各个区域,分了5个类:分别是:

 

ColorPanel:用以初始化颜色面板,并且实现颜色窗口的功能。

DrawingPanel:画图区域面板的实现类,用来获取画布

DrawingListener:监听器,用来画图,

DrawingMain:确定窗口主体,初始化窗口

ToolsPanel:初始化工具栏面板,确定绘图工具类型

      思路如下:将监听器添加在颜色按钮和工具按钮上,然后来确定绘图的工具类型和颜色类型,然后,画布从DrawingPanel上传到DrawingListener上,在DrawingListener里,获取鼠标的坐标点,通过各个工具类型所代表的绘图方法,在画布上绘画。

 

      具体代码如下:

ColorPanel:

package cn.javanet.drawborder;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

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

/**
 * 创建一个颜色面板类,这个类同样继承自JPanel类
 * @author 标准版~~~
 *
 */
public class ColorPanel extends JPanel{
	
	//声明前后景色的颜色为属性,分别默认为黑色和白色
	public Color colorF = Color.BLACK;
	public Color colorB = Color.WHITE;
	private MouseListener l;
	private String s = "要获得帮助,请在“帮助”菜单栏中,单击'帮助主题'";
	/**
	 * 构造方法
	 */
	public ColorPanel(){
		//调用初始化面板界面的方法
		init();
	}

	//初始化界面的方法
	public void init() {
		//设置整个下方面板的布局为流式布局,并且组件从左至右显示
		this.setLayout(new FlowLayout(FlowLayout.LEFT));

		//设置布局为空
		//this.setLayout(null);
		this.setPreferredSize(new Dimension(300,80));
		//创建一个前后景色颜色面板
		JPanel pa1 = new JPanel();
		//设置颜色面板的大小
		pa1.setPreferredSize(new Dimension(40,40));
		//设置该面板的布局为空
		pa1.setLayout(null);
		//设置该面板的背景颜色
		pa1.setBackground(Color.WHITE);
		//实例化一个前景色按钮对象
		final JButton jbF = new JButton();
		//实例化一个后景色按钮对象
		final JButton jbB = new JButton();
		//设置前后景色按钮的背景
		jbF.setBackground(Color.BLACK);
		jbB.setBackground(Color.WHITE);
		//设置两个按钮的位置
		jbF.setBounds(5, 5, 15, 15);
		jbB.setBounds(10, 10, 15, 15);
		//将两个按钮添加到面板上面来
		pa1.add(jbF);
		pa1.add(jbB);
		//该面板添加到当前面板上来
		this.add(pa1);
		//pa1.setBounds(0, 0, 40, 40);
		
		//匿名内部类为前后景颜色的选择添加监听器
		MouseListener l = new MouseListener(){
			/**
			 * 鼠标按下时执行的方法
			 */
			public void mousePressed(MouseEvent e){
				//强制类型转型,获取事件源对象——按钮事件
				JButton jbu = (JButton)e.getSource();
				//判断按下的键是左键还是右键
				//如果按下的左键,则改变前景色的颜色
				if(e.getButton() == 1){
					//获取当前按钮上的颜色
					colorF = jbu.getBackground();
					//改变前景色按钮上的颜色
					jbF.setBackground(colorF);
				}
				//如果按下的是右键,则改变后景色的颜色
				else if(e.getButton() == 3){
					//获取当前按钮上的颜色
					colorB = jbu.getBackground();
					//改变后景色按钮上的颜色
					jbB.setBackground(colorB);
				}
			}

			public void mouseClicked(MouseEvent e) {
				
			}

			public void mouseEntered(MouseEvent e) {
				
			}

			public void mouseExited(MouseEvent e) {
				
			}

			public void mouseReleased(MouseEvent e) {
				
			}
		};
		
		//创建一个选取颜色的面板
		JPanel pa2 = new JPanel();
		//设置颜色面板的大小
		pa2.setPreferredSize(new Dimension(280,38));
		//pa2的布局设置为网格,并且设置按钮间的间距
		pa2.setLayout(new GridLayout(2,14,3,3));
		//实例化一个颜色数组
		Color []a = {new Color(64,0,0),Color.BLACK,Color.WHITE,
		new Color(255,128,128),new Color(255,255,128),
		new Color(128,128,192),new Color(255,0,255),new Color(128,64,64),
		new Color(0,128,255),new Color(255,128,192),new Color(255,128,255),
		new Color(255,0,0),new Color(255,255,0),new Color(128,255,0),
		new Color(0,255,64),new Color(0,255,255),new Color(0,128,192),
		new Color(255,128,64),new Color(0,255,0),new Color(0,128,128),
		new Color(0,64,128),new Color(128,128,255),new Color(128,0,64),
		new Color(255,0,128),new Color(128,0,0),new Color(255,128,0),
		new Color(0,128,0),new Color(0,128,64),new Color(0,0,255),
		new Color(0,0,160),new Color(128,0,128),new Color(128,0,255)};
		//用循环将颜色按钮添加到面板2上
		for(int i = 0;i<a.length;i++){
			//实例化一个颜色按钮对象
			JButton jb = new JButton();
			//设置颜色按钮的大小
			jb.setPreferredSize(new Dimension(15,15));
			//给按钮设置背景颜色
			jb.setBackground(a[i]);
			//给按钮添加鼠标监听器,进行事件处理
			jb.addMouseListener(l);
			//将此按钮添加到颜色面板上
			pa2.add(jb);
		}
		//将该面板添加到当前面板上来
		this.add(pa2);
		//pa2.setBounds(45, 40, 280, 38);
	
		//设置一个空面板,
		//JPanel j = new JPanel();
		//j.setPreferredSize(new Dimension(20,40));
		//this.add(j);
		
		//创建一个面板,里面有一个标签,里面写明了帮助文字栏的内容
		JPanel pb=new JPanel();
		pb.setLayout(new FlowLayout(FlowLayout.LEFT));
		pb.setPreferredSize(new Dimension(700,20));
		JLabel jl = new JLabel(s); 
		pb.add(jl);
		//pb.setBounds(0, 45, 300, 10);
		this.add(pb);
	}	
}

DrawingListener:

package cn.javanet.drawborder;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

import javax.swing.JPanel;

/**
 * 一个事件处理类,该类实现了MouseListener、MouseMotionListener两个事件监听器处理接口
 * @author 标准版~
*/
public class DrawingListener extends MouseAdapter{

	//声明画布为属性
	private Graphics g;
	//因为只有Graphics2D才有调节字体像素的方法
	private Graphics2D gg;
	private int x1,y1,x2,y2;
	private ToolPanel tp;
	private ColorPanel cp;
	private int x3,y3,x4,y4,x,y;
	boolean flag = true;
	//默认一开始的颜色为黑色
	private Color color = Color.BLACK;
	//默认画的图形为直线
	private String shape_command = "line";
	
	//定义一个存储直线坐标的数组----定义为静态属性,因此可以直接被类名引用来访问
	static  int []array = new int[100000];
	//定义一个标志量,使得被画的多个图形都能保存
	private int i = 0;
	
	/*
	 * 构造方法,用于传入参数
	 */
	public DrawingListener(ToolPanel tp,ColorPanel cp,JPanel jp){
		this.tp = tp;
		this.cp = cp;
		g = jp.getGraphics();
	}

	/**
	 * 当鼠标点击时,执行的方法
	 */
	public void mousePressed(MouseEvent e) {
		//获取鼠标按下时x1、x2的坐标
		x1 = e.getX();
		y1 = e.getY();
		//如果按下的键是左键,则画图的颜色为前景色按钮的颜色
		if(e.getButton() == 1){
			color = cp.colorF;
		}//如果按下的键是右键,则画图的颜色为后景色按钮的颜色
		else if(e.getButton() == 3){
			color = cp.colorB;
		}
	}
	
	/**
	 * 当鼠标释放时,执行的方法
	 */
	public void mouseReleased(MouseEvent e) {
		//获取画图时的颜色
		g.setColor(color);
		//根据判断来画图形
		if(tp.shape_command.equals("line")){
			x2 = e.getX();
			y2 = e.getY();
			//i自加1
			i++;
			//将画直线中的各坐标值存进数组中保存,放入内存里,重绘时就能绘制出原来的图形了
			array[4*i-4] = x1;
			array[4*i-3] = y1;
			array[4*i-2] = x2;
			array[4*i-1] = y2;
			//调用画布对象画直线的方法
			g.drawLine(x1, y1, x2, y2);
		}else if(tp.shape_command.equals("oval")){
			//调用画布对象画椭圆或者圆的方法
			x2 = e.getX();
			y2 = e.getY();
			g.drawOval(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2));
		}else if(tp.shape_command.equals("rect")){
			//调用画布对象画矩形的方法
			x2 = e.getX();
			y2 = e.getY();
			g.drawRect(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2));
		}else if(tp.shape_command.equals("roundrect")){
			//调用画布对象画圆角矩形的方法
			x2 = e.getX();
			y2 = e.getY();
			g.drawRoundRect(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2),10, 10);
		}else if(tp.shape_command.equals("fillrect")){
			//调用画布对象画填充矩形的方法
			x2 = e.getX();
			y2 = e.getY();
			g.fillRect(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2));
			//获取填充图形的颜色
			g.setColor(color);
		}else if(tp.shape_command.equals("polygon")){
			//flag的作用是干嘛的,忘了····
			if(flag){
				x=x1;y=y1;
				x3 = e.getX();
				y3 = e.getY();
				g.drawLine(x1, y1, x3, y3);
				flag = false;
			}
			if(flag == false){
				x4 = e.getX();
				y4 = e.getY();
				g.drawLine(x3, y3, x4, y4);
				x3 = x4;
				y3 = y4;
			}
			if(e.getClickCount() == 2){
				g.drawLine(x, y, x4, y4);flag=true;
			}
		}else if(tp.shape_command.equals("frect")){
			x2 = e.getX();
			y2 = e.getY();
			g.fillRoundRect(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2),10,10);
		}else if(tp.shape_command.equals("foval")){
			x2 = e.getX();
			y2 = e.getY();
			g.fillOval(Math.min(x1,x2),Math.min(y1,y2), Math.abs(x1-x2),Math.abs(y1-y2));
		}
	}

	/**
	 * 当鼠标按下并拖动时,执行的方法
	 */
	public void mouseDragged(MouseEvent e) {
		x2 = e.getX();
		y2 = e.getY();
		//获取画图时的颜色
		g.setColor(color);
		//根据判断不同画图形
		//如果要画的是刷子时
		if(tp.shape_command.equals("brush")){
			//实例化一个字体像素对象
			BasicStroke bs = new BasicStroke(8);
			//强制转型,Graphics2D对象才能够调用设置像素的方法
			gg = (Graphics2D)g;
			//设置画的像素
			gg.setStroke(bs);
			g.drawLine(x1, y1, x2, y2);
			x1 = x2;
			y1 = y2;
			//恢复到原来的像素大小
			BasicStroke bs1 = new BasicStroke(1);
			gg.setStroke(bs1);
		}//如果要画的图像时铅笔时
		if(tp.shape_command.equals("pencil")){
			BasicStroke bs = new BasicStroke(1);
			gg = (Graphics2D)g;
			gg.setStroke(bs);
			g.drawLine(x1, y1, x2, y2);
			x1 = x2;
			y1 = y2;
		}//如果要实现橡皮的方法
		if(tp.shape_command.equals("easer")){
			g.setColor(Color.WHITE);
			BasicStroke bs = new BasicStroke(8);
			gg = (Graphics2D)g;
			gg.setStroke(bs);
			g.drawLine(x1, y1, x2, y2);
			x1 = x2;
			y1 = y2;
			//恢复到原来的像素大小
			BasicStroke bs1 = new BasicStroke(1);
			gg.setStroke(bs1);
		}//实现喷枪的方法
		if(tp.shape_command.equals("spray")){
			Random ra = new Random();
			for(int i = 0;i < 15;i++){
				int tempx = ra.nextInt(5);
				int tempy = ra.nextInt(5);
				g.drawLine(tempx+x2, tempy+y2, x2+tempx, y2+tempy);
			}
		}
	}
}

DrawingMain: 

package cn.javanet.drawborder;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;

/**
 * 创建一个主窗体类,继承自JFrame类
 * @author 彭豪英												
 *										
 */
public class DrawingMain extends JFrame{

	/**
	 * @param 主函数
	 */
	public static void main(String[] args) {
		//实例化一个主窗体类对象
		DrawingMain dm = new DrawingMain();
		//调用实例化窗体的方法
		dm.init();
	}

	//实例化主窗体界面的方法
	private void init() {
		//设置窗体的标题
		this.setTitle("仿XP画板");
		//设置主窗体的大小
		this.setSize(new Dimension(700,600));
		//设置窗体为边框布局
		this.setLayout(new BorderLayout());
		//设置窗体居中显示
		this.setLocationRelativeTo(null);
		//设置窗体被关闭之后程序停止运行
		this.setDefaultCloseOperation(3);
		//设置窗体不可以调节大小
		this.setResizable(true);
		
		//实例化一个菜单栏对象
		JMenuBar jmb = new JMenuBar();
		//将该菜单栏添加到主窗体上
		this.setJMenuBar(jmb);
		//用循环创建一个菜单对象
		String [][]a = {{"文件(F)","新建","打开","保存","另存为"},{"查看(v)"},
				{"图像(I)"},{"颜色(C)"},{"编辑(E)","复制","剪切","粘贴"},
				{"帮助(H)","帮助主题","关于画图"}};
		for(int i = 0;i < a.length;i++){
			//创建一个菜单对象
			JMenu jm = new JMenu(a[i][0]);
			jmb.add(jm);
			for(int j = 1;j<a[i].length;j++){
			JMenuItem jmi = new JMenuItem(a[i][j]);
			jm.add(jmi);
			}
		}
		
		//实例化一个工具面板对象,并添加到窗体上
		ToolPanel tp = new ToolPanel();
		this.add(tp,BorderLayout.WEST);
		
		//实例化一个颜色面板对象,并添加到窗体上
		ColorPanel cp = new ColorPanel();
		this.add(cp,BorderLayout.SOUTH);
		
		//实例化一个中心画图面板对象,并添加到窗体上
		DrawingPanel dp = new DrawingPanel();
		this.add(dp,BorderLayout.CENTER);
		
		//实例化一个画图的面板对象
		JPanel panel= dp.getJPanel();
		
		//设置该窗体可见,一定要在获取画布对象之前设置窗体可见;有了面板才能获取画布!
		this.setVisible(true);
		//创建一个继承了MouseAdapter抽象类的对象
		DrawingListener dl = new DrawingListener(tp,cp,panel);
		//为画图的面板添加鼠标监听器
		panel.addMouseListener(dl);
		//为画图的面板添加鼠标动作监听器
		panel.addMouseMotionListener(dl);
	}
}

DrawingPanel:

 package cn.javanet.drawborder;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;

import javax.swing.JPanel;

/**
 * 创建一个中心的画板面板类
 * @author 标准版~
 */
public class DrawingPanel extends JPanel{
	
	/*
	 *构造方法 
	 */
	public DrawingPanel(){
		//调用初始化中心画板面板界面的方法
		init();
	}
	JPanel jp ;
	public JPanel getJPanel(){
		return jp;
	}
	//初始化中心画板面板界面的方法
	private void init() {
		//设置本身的大面板的背景颜色为灰色
		this.setBackground(Color.gray);
		//设置面板的布局为流式布局
		this.setLayout(new FlowLayout(FlowLayout.LEFT));
		//用来获取画布的组件是哪个,就要重写哪个组件的paint方法实现重绘。
		jp = new JPanel(){
			//重写父类画图的方法
			public void paint(Graphics g){
				super.paint(g);
				System.out.println("paint()");
				//if(){
				 for(int i=0;i<DrawingListener.array.length/4;i++){
					 g.drawLine(DrawingListener.array[4*i],DrawingListener.array[4*i+1],
				     DrawingListener.array[4*i+2],DrawingListener.array[4*i+3]);
				 }
				//}
			}
		};
		jp.setPreferredSize(new Dimension(480,380));
		//设置该面板的背景颜色
		jp.setBackground(Color.white);
		this.add(jp);
	}
}

ToolsPanel:

 package cn.javanet.drawborder;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

/**
 * 创建一个工具面板类,这个类继承自JPanel类
 * 
 * @author 标准版~
 * 
 */
public class ToolPanel extends JPanel {

	//声明一个存储用户工具选择的图形属性
	public String shape_command = "line"; 

	//该类的构造方法
	public ToolPanel() {
		// 调用初始化该面板的方法
		init();
	}

	// 初始化工具面板界面的方法
	private void init() {
		// 设置ToolPanel的布局为流式布局
		this.setLayout(new FlowLayout());
		this.setPreferredSize(new Dimension(70, 150));
		// 创建一个工具栏面板
		JPanel pa = new JPanel();
		// 设置工具栏面板的布局为网格布局
		pa.setLayout(new GridLayout(7,2,5,5));
		// 用匿名内部类的形式来获取工具按钮上的命令,并且传给事件处理类对象来处理
		ActionListener l = new ActionListener() {
			/**
			 * 事件处理的方法
			 */
			public void actionPerformed(ActionEvent e) {
				//得到工具栏标签图片上的命令值,并存储到shape_command中
				shape_command = e.getActionCommand();
				System.out.println("获取的动作命令值shape_command="+shape_command);
			}

		};
		// 创建一个工具数组,里面放着图标所对应的路径
		String[] a = { "src/brush.jpg", "src/easer.jpg", "src/fillrect.jpg",
				"src/line.jpg", "src/oval.jpg", "src/pencil.jpg",
				"src/polygon.jpg", "src/rect.jpg", "src/roundrect.jpg",
				"src/spray.jpg","src/frect.jpg","src/foval.jpg"};
		// 在工具栏面板上添加按钮
		for (int i = 0; i < a.length; i++) {
			ImageIcon im = new ImageIcon(a[i]);
			// 实例化各个按钮对象
			JRadioButton jb = new JRadioButton(im);
			// 设置按钮的大小,建议最好用封装类
			jb.setPreferredSize(new Dimension(25, 25));
			//获取动作命令:图形的名字,截取动作命令值其中的某一段并存放在toolName里
			String toolName = a[i].substring(a[i].indexOf("/")+1, a[i].lastIndexOf(".jpg"));
			//设置按钮的动作命令值
			jb.setActionCommand(toolName);
			// 给工具按钮添加监听器
			jb.addActionListener(l);
			// 将按钮添加到工具栏面板上
			pa.add(jb);
			
		}
		// 将该工具栏面板添加到当前ToolPanel面板上
		this.add(pa);
	}

}

      这个程序在写的时候,我遇到的主要的技术难点还是在于画布工具按钮在设置了图标以后,命令内容的获取,这一块比较难,这里,用substring方法截取动作命令中的某一字段并存储。

      重绘的实现也是一个比较难的地方,重绘的方法,老师一共教了三种方法:

一、将每一次画出来的图形都存储进一个二维数组中,然后在窗体初始化的方法中,将存储在数组中的图形以及具体的参数拿出来重画,实现重绘。这种方法的缺点是,无法确定数组的大小

二、在每一次执行完一个绘图操作之后,将画布画面截下来,将每个像素点的颜色存储进一个自定义队列中,然后在窗体初始化的方法中,将存储在数组中的每个像素点的颜色,按每个点画在画布上,实现重绘。

三、将每一次绘画的操作的鼠标的起始点等参数均存入数组中,然后在初始化界面的时候重绘,这种方法,每一个图形绘图的方法都自成一个类,然后在各自的类中将图形的参数存储在数组中。

在这个程序的编程过程中。。。。我由于绕了很多弯路也有了一些心得:在编程过程中,有一个清晰的思路和明确,科学合理的结构会使编程只是一个敲代码的过程,而且,能保证代码的质量。明晰的思路,才是一个程序的灵魂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值