好久没有总结了,总结确实应该是经常性的,不然先前觉得很惊天动地的收获时间久了甚至都会忘记,这次的优化主要在于这几个方面,首先是功能的完善,到写博客为止,已经实现的功能有直线,矩形,椭圆,刷子,橡皮,铅笔以及各种颜色的选择和以上图形的重绘功能,将要实现的功能是喷漆,多边形,圆角矩形,曲线等,其中圆角矩形比较简单,当然,也包括以上形状的重绘,还有一些细节的优化,好吧,就先展望到现在,现在来总结一下这阶段的收获,首先这个阶段其实最重要的是重绘功能的实现,右最开始的保存坐标和标志来重绘,到后来的通过保存图形对象来实现,第一次这么深刻地体会到面向对象的思想具体实现。至于其他各种功能的实现,其实最重要的是思维逻辑问题来实现那些功能,这些其实不用斌哥一个一个地讲,最重要的是自己要好好想清楚怎么实现。

首先回顾一下第一阶段重绘的实现,在这个阶段,重绘是通过定义一个自定义队列,用来保存和取其中的元素,考虑到数组只能设置固定大小,所以设置自定义队列用来实现能够动态改变数组的大小,其具体的实现是这样的:

public class MyList{
	
	 
	 Shape[] srcArry=new Shape[0];
	  
	  public void add( Shape e){
		  Shape[] destArry=new  Shape[srcArry.length+1];
		 for(int i=0;i<srcArry.length;i++){
			 destArry[i]=srcArry[i];
		 }
		    destArry[srcArry.length]=e;
			 srcArry=destArry;
			 
	  }
	   public  Shape get(int i){
		   return srcArry[i];
		 
	  }
	   public int getLength(){
		   return srcArry.length;
		 
	  }

	 
 }

然后实现重绘的功能是通过在每次画图之后保存坐标和标志,因为坐标和标志是不同类型,所以在设置自定义队列的时候要把类型定义为Object类,这样可以保存不同类型的元素,等到用的时候再通过强制转型把他转换成相应的类型就行,这样元素的保存与取用实现了,关键在于怎样保存坐标和怎样在重绘方法里面取用坐标,首先保存坐标,在每次画图之后保存画图的四个坐标,即两个点,然后还有command标志和color,保存的操作是这样的:

public void mouseReleased(MouseEvent e) {
	  if("line".equals(command)||"rect".equals(command)||"oval".equals(command)){ 
			x2 = e.getX();
			y2 = e.getY();
		    BasicStroke basicstroke=new BasicStroke(1);
			Graphics2D gr=(Graphics2D) g;
			gr.setStroke(basicstroke);
			mylist.add(x1);
			mylist.add(y1);
			mylist.add(x2);
			mylist.add(y2);
	     if("line".equals(command)){
			g.drawLine(x1,y1,x2,y2);
		 }else if("rect".equals(command)){
		    g.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
		 }else if("oval".equals(command)){
			g.drawOval(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
		 }
		   mylist.add(color);
    }
}



在重绘的时候要顺次取出这些元素来重绘,在这里‘’顺序‘’是非常重要的,怎么保存就怎么取出来,这样才能一一对应,不然的话会出现classcastexception这个异常,这种异常是类型转换时候的错误,问题在于如果存的时候存的是整形那么取得时候如果取colour类型的就会出现这样的错误,在重绘里面取坐标进行重绘的操作是这样的:

//	public void paint(Graphics g){
//		
//		super.paint(g);
//		for(int i=0;i<mylist.getLength()/6;i++){
//			int x1 = (Integer)mylist.get(6*i);
//			int y1 = (Integer)mylist.get(6*i+1);
//			int x2 = (Integer)mylist.get(6*i+2);
//			int y2 = (Integer)mylist.get(6*i+3);
//			String command = (String)mylist.get(6*i+4);
//		    Color color = (Color)mylist.get(6*i+5);
//			g.setColor(color);
//			if("line".equals(command)){
//				g.drawLine(x1,y1,x2,y2);
//			}else if("rect".equals(command)){
//				g.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
//			}else if("oval".equals(command)){
//				g.drawOval(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
//			}else if("pencil".equals(command)){
//				g.drawLine(x1,y1,x2,y2);
//			}
//		}
//	}


这样之后就能实现重绘的功能了,但是这样尽管尽管能够实现但是会存在问题,首先就是代码的冗长和杂乱,坐标随处可见,而且万一以后的图形不是用两个坐标实现的话那么又会出现问题,所以想到了要用面向对象里的思想,就是把自定义队列里面的每一个元素保存为每一个图形对象,这样子的话实现操作便会很简洁了,画一个图形的话就保存一个图形,然后重绘的时候就依次取出来每个图形对象就行,思路很清晰,但是要实现的话要做一些准备工作,既然要把自定义队列里面的元素设置为图形类型,那我们肯定要定义一个图形类型,shape类,考虑到有好几中图形,那我们不妨定义一个根类型,然后让其他图形类型比如直线,矩形,椭圆来继承他就行,这里又用到了抽象类的知识,我们把那个根类型shape定义为abstract类型,具体定义是这样的:

public abstract class Shape{
   public int x1,y1,x2,y2;
   public Graphics g;
   public Color color;
   public int width;
   public  Shape(int x1,int y1,int x2,int y2,Color color,int width){
        this.x1=x1;
        this.y1=y1;
        this.x2=x2;
        this.y2=y2;
        this.color=color;
        this.width=width;
     } 
   public abstract void draw(Graphics g);
 }

这样就把根类型定义好了,之后就是实现几种子图形的类了,比如直线,现在以直线为例,他的实现是这样的:

public class Line extends Shape{
  
  public Line(int x1,int y1,int x2,int y2,Color color,int width){
   super (x1,y1,x2,y2,color, width);
  }
  public void draw(Graphics g){
   g.setColor(color);
   g.drawLine(x1, y1, x2, y2);
  }
 }

之后的操作就很简单了,每次画图之后保存一个这样的对象到自定义队列里面,然后重绘的时候再依次取出来,具体的实现是这样子的:

public void mouseReleased(MouseEvent e) {
    if("line".equals(command)||"rect".equals(command)||"oval".equals(command)){ 
    
       x2 = e.getX();
    y2 = e.getY();
     
       if("line".equals(command)){
    shape=new Line(x1,y1,x2,y2,color, 1);
    BasicStroke basicstroke=new BasicStroke(shape.width);
    Graphics2D gr=(Graphics2D)g;
    gr.setStroke(basicstroke);
    shape.draw(gr);
    }else if("rect".equals(command)){
     shape=new Rect(x1,y1,x2,y2,color,1);
      BasicStroke basicstroke=new BasicStroke(shape.width);
     Graphics2D gr=(Graphics2D)g;
     gr.setStroke(basicstroke);
     shape.draw(gr);
    }else if("oval".equals(command)){
     shape=new Oval(x1,y1,x2,y2,color, 1);
     BasicStroke basicstroke=new BasicStroke(shape.width);
     Graphics2D gr=(Graphics2D)g;
        gr.setStroke(basicstroke);
     shape.draw(gr);
    }
       mylist.add(shape);
     }
 }

这样子就把每次的图形对象保存到自定义队列里面了,取出来进行重绘的时候是这样子的:

public void paint(Graphics g){
		
		super.paint(g);
		for(int i=0;i<mylist.getLength();i++){
			if(mylist.get(i).width==1){
				BasicStroke basicstroke1=new BasicStroke(mylist.get(i).width);
			    Graphics2D gr=(Graphics2D) g;
			    gr.setStroke(basicstroke1);                  
				mylist.get(i).draw(gr);
			                      
		    }
			if(mylist.get(i).width==5){
				BasicStroke basicstroke2=new BasicStroke(mylist.get(i).width);
			    Graphics2D gr=(Graphics2D) g;
			    gr.setStroke(basicstroke2);
			    mylist.get(i).draw(gr);
			    
			}
		}
	}

这样子就实现了重绘的功能,相对于上一种方法,这种方法的条理更加清晰,其实学习的过程就是这样一步一步地完善的,现在再来说明一下喷漆的实现,这里要用到随机数的方法,每次在mousedragged方法里面会获取一个坐标x2,y2,我们可以以这个点为基准点,然后产生一个随机点,这个随机点的横坐标是-a+x2到+a+x2,纵坐标是-a+y2到+a+y2,就是以基准点为中心的一个正方形里面随机取点,这样就能得到喷漆的功能,其他重绘的方法都类似,这里不再赘述,下面是随机点的获取方法:



else if("airbrush".equals(command)){
	    	 int x2 = e.getX();
	    	 int y2 = e.getY();
	    	Random random = new Random();
	    	for(int k=0;k<50;k++){
	    	int i = random.nextInt(20);
	    	int j = random.nextInt(20);
	    	int x = x2+(10-i);
	    	int y = y2+(10-j);
	    	shape=new Line(x,y,x,y,color,1);
	    	shape.draw(g);
	    	mylist.add(shape);
	    	}

接下来还要继续优化一些细节问题,会对比着系统的画图板来完善,继续加油!!!