`
xiaoyjj
  • 浏览: 52500 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

画图板的完善——重绘

阅读更多

上一篇所总结的是实现画图板的界面以及基本功能。那么在我们画图的时候就会发现一个问题:我们画好了一个图形之后,如果改变的窗口大小,或者最大化最小化之后,我们所画的图形就都消失了!我们画的东西之所以会小时,是因为我们没有将所画的形状存放到内存中。而且我们在执行上述操作(改变窗体大小……)的时候会自动调用一个paint的方法。我们没有定义重绘paint的这个方法,所以在执行重绘的时候之前所画的都不存在了。所以使得自己所画图案永久存在的方法就是要在执行paint方法的时候再把我们所画的图案给他重新画一遍!这就是重绘。

重绘的方法有很多,我现在也只是能够完成使用一维数组或者二维数组这两种方法来进行重绘操作。

 

一维数组的重绘

     一维数组确实可以完成重绘方法,但很麻烦很麻烦而且又不稳定。这总结他只是简单的说下吧,因为他不实用。

我们在画图的时候最关键的几个要素就是:画什么,在哪里画,颜色。这三点,在哪里画的关键则是所取得的那些xy坐标。所以只要我们使用一个一维数组把所画图形坐标都给存储起来,再重绘的方法中再按照存储起来的坐标给重新绘制一遍,就完成了重绘的操作。只用直线举个例子吧:

新建一个一维数组 int [] array_line = new int[];那么我们在建立数组的时候发现问题了:数组长度设置为多少呢??这就是之前所说一维数组完成重绘的缺点,不仅仅是麻烦,而且如果我们在这里数组长度定义的较小,例如定义为100。那么我们存储25条直线之后再画的先就存储不下了。若是定义的过大则是对内存的浪费。。

先不管那些啦。看看一维数组怎样存储坐标再怎样还原吧:

public void mousePressed(MouseEvent e) {
	x1 = e.getX();
    	y1 = e.getY();
    	}

public void mouseReleased(MouseEvent e) {
                int x2 = e.getX();
    	int y2 = e.getY();
    	if(command.equals("line")){
    		//画一条直线   		
    		db.g.drawLine(x1,y1,x2,y2);
    		array_line[4*i_line] = x1;
    		array_line[4*i_line+1] = y1;
    		array_line[4*i_line+2] = x2;
    		array_line[4*i_line+3] = y2;
    		i_line++;
    	}

 这样我们就把每一条直线的坐标按照四个四个一组给存入到array_line这个数组中。

那么再重绘方法中是怎么写呢?

dpaper = new DrawPanel(dListener){public void paint(Graphics g){
	super.paint(g);
	for(int i = 0;i<DrawListener.array_line.length/4;i++){
                              g.drawLine(DrawListener.array_line[4*i], DrawListener.array_line[4*i+1], DrawListener.array_line[4*i+2], DrawListener.array_line[4*i+3]);
                }
};

  其中dpaper是画布的那块面板,这里我们使用了一个匿名内部类,其中super.paint(g);这一语句的作用是调用父类的paint方法,用来绘制窗体。  使用for循环,4个4个一组来获取array_line中的数据并用之来绘制直线就完成了重绘的操作。

很显然使用一维数组来弄重绘真的十分麻烦不方便。接下来就是用二维数组来进行重绘的操作

 

二维数组的重绘

     二维数组的重绘思想呢,大体上就是因为我们的计算机屏幕都是由一个个小的像素点所组成,画图板中间的那块画布也是由行多少个、竖多少个像素点的那样一个点阵所组成,所以设想一下如果我们利用一个二维数组讲每一个像素点的颜色存储起来,重绘的时候再把像素点的颜色还原回去,是不是也完成了重绘的功能呢?

接下来就来试一试吧:

这里需要使用之前没有用过的一个类Robot。他里面有一个方法:createScreenCapture。他的作用是用来截取屏幕上一部分图片。所以我们只要把画图板画布的部分使用这个方法截图下来,在一个一个点来扫面并将像素点的颜色数据存入一个二维数组就可以了:

public void getScrean(){
	point1 = DrawBoard.dpaper.getLocationOnScreen();
	Rectangle rect = new Rectangle((int) point1.getX(), (int) point1.getY(),DrawBoard.dpaper.getWidth(), DrawBoard.dpaper.getHeight());
	BufferedImage image = robot.createScreenCapture(rect);
		
	for(int i=0;i<image.getWidth();i++){
		for(int j=0;j<image.getHeight();j++){
		//将对应坐标点颜色值取出存入到二维数组中。
		DrawBoard.array_paint[i][j] = image.getRGB(i, j);
		}
	}
}

 其中有一个方法getLocationOnScreen。他的作用是获取画布的左上角坐标,之后再建立一个举行封装类用来作为createScreenCapture的参数,这样就把画布部分给用图片来保存下来了。

接下来在重写paint方法的时候只要一个个的将点颜色还原就好啦:

paper = new DrawPanel(dListener){
       public void paint(Graphics g){
	super.paint(g);
	for(int i=0;i<array_paint.length;i++){
		for(int j=0;j<array_paint[i].length;j++){
			Color color = new Color(array_paint[i][j]);
                                g.setColor(color);
			g.drawLine(i, j, i, j);
		}
	}
       }
};

  

这样二维数组的重绘就完成了。与一维数组的重绘相比较很显然这个可以说比那种方法要强上10086条街……

 

画图板画布重绘又出了新方法啦:队列重绘以及图片重绘。

 

先来说下队列重绘:

      在我们熟悉使用自定义队列之后,就可以发现自定义队列虽然说白了也就是一维数组,但他却具有一维数组很多不能比的优点。首先,我们定义数组的时候就要把数组的长度给设置好,并且在之后的操作中无法再改变数组的长度,因为java是无法操作系统内存的。队列则不同,他可以多次添加或者删除队列中的元素数据。数组还有一个缺点那就是定义了之后就只能存储一种数据类型的数据。然而队列只要我们使用泛型,就可以解决这个问题了。

      那么现在我们就使用队列来对画图板进行重绘的操作。先来了解一下大题思路:我们每次在画板上画的图形,无论是直线矩形或者是铅笔喷漆,都可以说是一个个的对象,那么我们就可以使用队列来把这些对象都存储起来,然后在重绘方法中从头遍历队列中的元素再把他们给重新画到画布上,这样就完成了队列的重绘。在做之前,我们先要有一些准备:

 

      1.首先自然而然要有一个自定义队列:

public class DuiLie {
	Object [] list = new Object[0];
	
	/*
	 * 向队列中加入元素
	 */
	public void  add(Object m){
		//创建一个新的数组,长度为原数组长度+1
		Object [] newlist = new Object[list.length+1];
		//将原数组中的元素存入新的数组当中
		System.arraycopy(list, 0, newlist, 0, list.length);
		newlist[list.length] = m;
		
		list = newlist;
	}
	
	/*
	 * 获取队列长度
	 */
	public int getSize(){
		return list.length;
	}
	
	/*
	 * 得到某一索引位置上的元素的方法
	 */
	public Object getItem(int index){
		return list[index];
	}
	
}

 因为我们使用队列重绘只需要向队列中加入图形的对象,然后在遍历的时候需要知道队列的长度以及获得每一个位置的数据内容,所以在这个自定义队列中就这些了这三个方法。~

 

      2.我们要给那些图形创建类

      我们要把那些图形当成对象存储起来,所以就要创建一个图形类,然后不同的图形呢,只需要继承这个类就可以了。

 

 

import java.awt.Color;
import java.awt.Graphics;

public abstract class Shape {
	/*
	 *定义属性 
	 */
	public int x1,x2,y1,y2;
	public Color color;
	
	/*
	 * 重载构造方法
	 */
	public Shape(int x1,int y1,int x2,int y2,Color color){
		this.x1 = x1;
		this.x2 = x2;
		this.y1 = y1;
		this.y2 = y2;
		this.color = color;
	}
	
	public abstract void draw(Graphics g);
	
}

 这是定义的一个图形类,其中包含了五种属性,分别是我们在画图时获取的始末点的xy坐标以及画图时所选的颜色。

 

接下来就只拿直线和矩形这两个为例子来看看每个不同工具绘制图案的图形类怎么创建吧

import java.awt.Color;
import java.awt.Graphics;

public class Line extends Shape{

	public Line(int x1, int y1, int x2, int y2, Color color) {
		super(x1, y1, x2, y2, color);
	}

	public void draw(Graphics g) {
		g.setColor(color);
		g.drawLine(x1, y1, x2, y2);
	}

}

 

 

 

import java.awt.Color;
import java.awt.Graphics;

public class Rect extends Shape{

	public Rect(int x1, int y1, int x2, int y2, Color color) {
		super(x1, y1, x2, y2, color);
	}

	public void draw(Graphics g) {
		g.setColor(color);
		g.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x1-x2),Math.abs(y1-y2));
	}
	

}

  这两个类就是分别用来存储我们画直线和矩形时所画的图形的类。下面只要在监听器里面修改下就可以把这些图形存储起来:

    if(command.equals("line")){
      //画一条直线     
      shape = new Line(x1,y1,x2,y2,col);
    
     }
     else if(command.equals("rect")){
      //画一个矩形
      shape = new Rect(x1,y1,x2,y2,col);
     }

shape.draw(db.g);
list.add(shape);

 这样就是在我们画出图形的同时,又把那些图形存到shape中,并且添加到我们事先定义好的list队列中。

      这些准备工作都做好了,接下来就是据图的重绘操作,也很简单:

 

dpaper = new DrawPanel(dListener){
	public void paint(Graphics g){
		super.paint(g);
		for(int i=0;i<DrawListener.list.getSize();i++){
			//把图形对象取出来,画图		
			((Shape)DrawListener.list.getItem(i)).draw(g);
			}
	}
			
};

 只要在这里重写一下重绘方法,使用循环遍历队列中的元素,并将每一个元素画出就可以啦。

      这样队列重绘就完成了。

 

 

除了这个还有一中更为简单的重绘的方法:图片重绘。

这种方法与我们之前使用的二维数组的重绘很相似,二维数组的重绘是通过把我们画布给作为一张图片截取下来,之后把那张图片每一个像素的颜色都存储起来,并在重绘的时候一个个绘制上去来完成的,而图片重绘则是直接把整个图片都重新贴上去一样。这里就只要用到一个方法:

drawImage(Image img, int x, int y, ImageObserver observer);

      这个方法是Graphics中的一个,他就是在指定位置绘制一张图片。所以我们使用图片重绘只需把之前用二维数组做重绘所截取的图片使用这个方法在重绘的时候贴到画布上就好了。但这里有一个需要并且容易犯错误的地方:就是我们重绘这张图片的xy坐标是什么。就是那个函数中的第二和第三个参数。看API文档很显然可以知道xy指的是:图像的左上角位于该图形上下文坐标空间的 (xy)。所以我们在画布上进行重绘,所以所用的xy坐标都只要为0就行了。这里不要误以为是我们的画图板在屏幕上的坐标,那样就会出先重绘时候图片位置不对的情况的。

 

 

 

这样~~我们所画的各种图案~只要不关闭窗口就不会再随着最大化或者最小化而消失啦!

 

这样我所知道的4种重绘方法就都已经在这里了,再对这些方法进行一个小小的总结与比较呢:~

      显然,第一种一维数组重绘是最不好的,我觉得可以这么说,因为如果我们把数组定义的过小,那么绘制图片较多的时候后面绘制的就存不下了,如果定义的较大则是对内存的一种浪费。

      二维数组重绘,相比以为数组确实有优点,但是这个重绘是一个一个点绘制,执行速度上肯定不会很快。

      队列的重绘就完全避免的一维数组重绘的缺点,但是我觉得那样每一个图形建一个类也好麻烦的。。。

      。。所以呢。我个人认为最好最简单的还是图片重绘。。嘿。

 

关于重绘的总结就这些吧。希望可以给看这篇文章的亲们点点帮助~~

 

2
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics