**
1. 为什么需要重绘:
**
原因:在自己定义的画图板上绘制图形的时候,缩小窗口拉大窗口的时候,窗体上的图形就会消失。当窗体大小发生改变的时候,系统会自动调用JFrame的Paint方法,因为我们在窗体上绘制的图形是存在缓存中的,当窗体的大小改变后,这些缓存会自动清空,重新调用paint方法,但是paint没有重绘这些图形,只重绘窗体上的组件。
1. 如何重绘
知道为什么需要重绘的根本原因之后,就可以针对问题解决了。首先我们用重新定义一个绘图的类绘制我们想要在窗体上绘制的图形 ,即不直接用画笔绘制图形到窗体上,而是利用我们自己定义的绘制图形的方法,(原因见下),再这些图形存在自己定义的队列(为什么选择队列呢,原因见下)中,最后是最重要的一步,重写组件(这里可以指代窗体JFrame)的paint方法,在方法中,我们将队列里的图形元素取出,逐一绘制在窗体上。
为何选择队列这种数据结构
选用队列,重要的原因是因为队列是一种具有先进先出特性的数据结构,在这个画图板项目中,确实是需要先进先出的,(举个例子:你在一条直线上画一个矩形,又在上面画一个椭圆,当你重画的时候,当然是先画直线,再画矩形最后画椭圆吧,这就是先进先出,先进入队列的先出队)。
为何要自己定义一个绘制图形的方法
将绘制图形抽象为一个类,不是必须的,但是是一个重要的思想,符合标称思想可以很好的简化代码,增加代码的复用性,(具体的体现,绘制的图形有多重多样的,将它抽象为一个类,每一个图形的沪指方法,都写成一个方法,在将每个图形的基本属性写成类的基本属性,哪里要绘制图形,调用它对应的方法即可)。
具体版块的代码实现
- 自定义的队列具体代码实现:
/**
* 自定义的 一个队列
* @author 朱小露
* * @param <E>
*/
public class MyList<E>{ //这里的E是泛型,new的时候需要指明存储的数据类型
private int length = 0; //队列的长度
//定义一个数组
public Object src[] = new Object[0]; //原始数组
/**
* 定义一个在队尾添加元素的方法
* @param e
*/
public void add(E e){
Object dest[] = new Object[src.length + 1]; //新建一个数组
dest[src.length] = e; //将新添加的元素放在队尾
//将原始数组复制到新建的数组
System.arraycopy(src, 0, dest, 0, src.length);
//将数组赋值给原始数组
src = dest;
//更新数组长度
length = src.length;
}
/**
* 定义一个按位取值的方法
* @param index 数组下标
* @return 存储的对象
*/
public E get(int index){
Object e = src[index];
return (E)e;
}
public int size(){ //获取队列的长度
return length;
}
}
- 将绘制图形的方法抽象出来,具体代码实现(这里只列举了矩形,直线,椭圆的绘制方法):
*
/**
*自定义的绘制图形的类
* @author 朱小露
* */
public class shape {
//图形的基本属性
private int x1;
private int y1;
private int x2;
private int y2;
private Color color;
private String name;
/**
* 利用构造方法初始化图形的属性
* @param x1 鼠标按下时的横坐标
* @param y1 鼠标按下时的纵坐标
* @param x2 鼠标释放时的横坐标
* @param y2 鼠标释放时的纵坐标
* @param color 画笔的颜色
*/
public shape(int x1, int y1, int x2, int y2, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
}
/**
*设置图形的名字,例如:直线,矩形,椭圆
* @param name
*/
public void setShape(String name){
this.name = name;
}
/**
* 获取图形的名字
* @return
*/
public String getShape(){
return this.name;
}
//画直线
public void drawLine(Graphics g){
g.drawLine(x1, y1, x2, y2);
}
//画矩形
public void drawRect(Graphics g){
g.drawRect(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x2-x1),Math.abs(y2-y1));//取绝对值
}
//画椭圆
public void drawOval(Graphics g){
g. drawOval (Math.min(x1, x2),Math.min(y1, y2), Math.abs(x2-x1),Math.abs(y2-y1));//取绝对值
}
}
- 鼠标监听事件里的鼠标按下和鼠标释放事件的代码实现:
/**
* 鼠标按下
*/
public void mousePressed(MouseEvent e) {
//获取鼠标按下时的横纵坐标
x1 = e.getX();
y1 = e.getY();
}
/**
* 鼠标释放
*/
public void mouseReleased(MouseEvent e) {
//获取鼠标释放时的横纵坐标
x2 = e.getX();
y2 = e.getY();
//创建绘制图形类的对象,并指定图像的属性
sh = new shape(x1,y1,x2,y2,color);
sh.setShape(command);
//创建自定义队列的对象,并指定存贮数据的类型
list = new MyList<shape>();
if(command.equals("直线")){ //画直线
sh.drawLine(g);//利用自定义的形状类来画图
list.add(sh);//将画出的形状加入到队列中
}
if(command.equals("矩形")){ //画矩形
sh.drawRect(g);
list.add(sh);
}
if(command.equals("椭圆")){ //画椭圆
sh.drawOval(g);
list.add(sh);
}
}
- 重写paint方法具体代码实现:
public void paint(Graphics g){//重写画图板的paint方法
super.paint(g);
//遍历队列,将放入队列的图形取出来
for(int i = 0; i< list.size(); i++){
shape sh = list.get(i);
if(sh.getShape().equals("直线")){
sh.drawLine(g);
}
else if(sh.getShape().equals("矩形")){
sh.drawRect(g);
}
else if(sh.getShape().equals("椭圆")){
sh.drawOval(g);
}
}
}