当我们写好一个简易画板,然后在上面画线,画圆等,再把窗口最小化,接着打开,你发现了什么??对,很神奇的事发生了,不是在做梦,之前的所有图形全部消失了,why??原来, 当窗体在屏幕上显示时,首先是将窗体对象的数据从内存中取出来放到缓存中,再在屏幕上进行绘制。当窗体发生改变的时候,程序会重新从内存中获取更新后的数据绘制。all effort are in vain!那怎么办呢?嗯嗯,方法当然是有的啦,就是重绘!!
通过重写JFrame类的paint(Graphics g)方法来保存我们画过的形状,大概就是下面这么个过程:
1)创建保存形状的队列(采用泛型)
2)每绘制一个形状,就将绘制过的形状保存到队列。
这里就要提到图形类,其实就是创建一个图形的类,里面有画这个图形的方法啦。不过在此前,要创建一个抽象的Shape类,让图形类去继承它;因为要创建很多图形类嘛,哈哈。
之后呢要在事件源上的g(Graphics类)进行重写paint()方法,把队列中存储的图形全部绘制出来,这样就搞定啦。不过要提醒的是,如果是在窗体上进行重写paint()方法,图形可能会越界到其他面板上哦!!
大概的代码如下:
首先是实现类:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Draw {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化一个画板对象
Draw sd=new Draw();
//调用显示画板的方法
sd.showUI();
}
public void showUI()
{
/*
* 实例化一个窗体对象
*/
JFrame jf=new JFrame();
jf.setTitle("普通画板");//设置窗体标题
jf.setSize(600,500);//设置窗体大小
jf.setLayout(new BorderLayout());//设置窗体布局为边框布局
jf.setResizable(false);//禁止窗体改变大小
jf.setLocationRelativeTo(null);//设置窗体居中
jf.setDefaultCloseOperation(3);//设置关闭窗体关闭程序
//调用创建北边面板方法
JPanel northpanel=creatNorthPanel();
//将面板添加到窗体上
jf.add(northpanel,BorderLayout.NORTH);
//调用创建中间面板方法
JPanel centerpanel=creatCenterPanel();
//将面板添加到窗体上
jf.add(centerpanel,BorderLayout.CENTER);
jf.setVisible(true);//设置窗体为可见 // 先让窗体可见,再弄下面的程序!!!!!!!!!!!!!!!!!!!!!!!!
Graphics g=centerpanel.getGraphics();//从中间面板中接收Graphics对象
DrawListener ml=new DrawListener(g,this);//实例化一个DrawListener类对象
//给中间面板添加动作监听器,绑定MouseListener事情处理类对象
centerpanel.addMouseListener(ml);
//给中间面板添加动作监听器,绑定MouseMotionListener事件处理类对象
centerpanel.addMouseMotionListener(ml);
}
private String str="Line";//定义一个存储图像的属性值
/*
* 获得存储图像的属性
*/
public String getStr()
{
return str;
}
private Color color;//定义颜色的属性值
/*
* 获得颜色的属性
*/
public Color getColor()
{
return color;
}
/*
* 创建北边面板的方法
*/
public JPanel creatNorthPanel()
{ JPanel north_panel=new JPanel();//实例化北边面板的对象
//设置北边面板的属性
north_panel.setPreferredSize(new Dimension(500,100));//设置北边面板大小
north_panel.setLayout(new FlowLayout(FlowLayout.LEFT));//设置北边面板布局为流式布局,并向左对齐
//匿名,实现ActionListener接口
ActionListener al=new ActionListener()
{
//事情发生类方法
public void actionPerformed(ActionEvent e)
{
//判断是否点击到"Color"按钮
if(e.getActionCommand().equals("Color"))
{ //弹出一个颜色选择器,供选择颜色;选择好的颜色返回color
color=JColorChooser.showDialog(null, "颜色选择器", Color.BLACK);
}
else
{//获取按钮上的文本
str=e.getActionCommand();//将获得的字符赋给字符串str
}
}
};//注意加“;”!!!!!!!!!!!!!!!!!!
//创建一位数组,储存按钮上的文本
String []array={"Line","Rectangle","Oval","RoundRect","Polygon","Pencil","Color","Eraser","Brush","Gunjet"};
// 遍历数组,创建按钮,并添加文本在按钮上
for(int i=0;i<array.length;i++)
{
JButton jb=new JButton(array[i]);//实例化按钮对象
jb.setPreferredSize(new Dimension(100,40));//设置按钮大小
//给事件源添加动作监听器方法,绑定事件处理类对象
jb.addActionListener(al);
north_panel.add(jb);//将按钮添加到北边面板上
}
return north_panel;
}
/*
* 创建中间面板的方法
*/
public JPanel creatCenterPanel()
{ JPanel center_panel=new JPanel(){
//重写paint()方法
public void paint(Graphics g)
{
super.paint(g);//调用父类的paint()方法
//绘制存储的图形
for(int i=0;i<DrawListener.list.size();i++)
{
Shape shape=DrawListener.list.get(i);//获取存储图像类
shape.draw(g);//调用图形类绘制方法
}
}
};//实例化中间面板的对象
center_panel.setBackground(Color.WHITE);//设置面板的背景色
return center_panel;
}
}
然后是数组队列的创建:
import java.util.Collection;
public interface SelfArrayList <E>{
public void add(E e);//向队列尾部添加元素方法
public boolean remove(E e);//移除此列表中首次出现的指定元素(如果存在)
public E get(int index);//取得队列中指定位置的一个对象
public int size();//得到队列长度
public void add(int index, E e); //将指定的元素插入此列表中的指定位置。
public boolean addAll(Collection<? extends E> c);//按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
public void clear();// 移除此列表中的所有元素
public int indexOf(E e);//返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
public boolean isEmpty();//如果此列表中没有元素,则返回 true
public int lastIndexOf(E e);//返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
public E set(int index, E e);// 用指定的元素替代此列表中指定位置上的元素。
public E remove(int index);//移除此列表中指定位置上的元素。
}
然后是Shape的抽象类:
import java.awt.Color;
import java.awt.Graphics;
public abstract class Shape {
private int x1,x2,y1,y2;//定义坐标属性
private Color color;//定义颜色属性
private int width;//定义宽度属性
private int height;//定义高度属性
//构造方法
public Shape(int x1,int x2,int y1,int y2,Color color,int width,int height)
{
this.x1=x1;
this.x2=x2;
this.y1=y1;
this.y2=y2;
this.color=color;
this.width=width;
}
public void draw(Graphics g)
{
}
public void setx1(int x1)
{
this.x1=x1;
}
public int getx1()
{
return x1;
}
public void setx2(int x2)
{
this.x2=x2;
}
public int getx2()
{
return x2;
}
public void sety1(int y1)
{
this.y1=y1;
}
public int gety1()
{
return y1;
}
public void sety2(int y2)
{
this.y2=y2;
}
public int gety2()
{
return y2;
}
public void setColor(Color color)
{
this.color=color;
}
public Color getColor()
{
return color;
}
public void setWidth(int width)
{
this.width=width;
}
public int getWidth()
{
return width;
}
public void setHeight(int height)
{
this.height=height;
}
public int getHeight()
{
return height;
}
}
然后是各种图形类,这里就只放一个直线类和橡皮擦类啦:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
/*
* 直线的图形类
*/
public class ShapeLine extends Shape {
//继承的子类要和父类有同一套构造方法
public ShapeLine(int x1, int x2, int y1, int y2, Color color, int width) {
super(x1, x2, y1, y2, color, width, width);
// TODO Auto-generated constructor stub
}
//画直线的方法
public void draw(Graphics g)
{
g.setColor(this.getColor());//设置图形颜色
Graphics2D g2=(Graphics2D)g;//强制转型,把Graphics类型转换成Graphics2D类型
g2.setStroke(new BasicStroke(this.getWidth()));//设置线条粗细
g2.drawLine(getx1(), gety1(), getx2(), gety2());//画直线
}
}
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class ShapeEraser extends Shape {
public ShapeEraser(int x1, int x2, int y1, int y2, Color color, int width,
int height) {
super(x1, x2, y1, y2, color, width, height);
// TODO Auto-generated constructor stub
}
public void draw(Graphics g)
{
g.setColor(this.getColor());//设置白色颜色
Graphics2D g2=(Graphics2D)g;//强制转型
//设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND
g2.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
g2.drawLine(getx1(),gety1(),getx2(),gety2());
g2.setStroke(new BasicStroke());
}
}
最后是事件监听器:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Random;
public class DrawListener implements MouseListener ,MouseMotionListener {
private int x1,x2,y1,y2;//记录鼠标两次点击的坐标
private Graphics g;//从界面对象上得到的画布对象
private Graphics2D gg;
private Draw sd;
private int flag=1;
public int m,n;
public int x3,y3,x4,y4;
private int m1=0,n1=0;
//创建一个数组队列,存储图像类
public static UseArrayList<Shape> list=new UseArrayList<Shape>();
//构造方法,用来接收Graphics对象和Draw类对象
public DrawListener(Graphics g,Draw sd){
this.g=g;
this.sd=sd;
}
//鼠标按下的点的坐标
public void mousePressed(MouseEvent e){
//记录第一次点击的x,y:通过事件对象e得到的
x1=e.getX();
y1=e.getY();
//获得点击“Polygon”时初次点击的点的坐标
if(sd.getStr().equals("Polygon"))
{
x3=e.getX();
y3=e.getY();
flag++;
if(flag==2)
{
m=x3;
n=y3;
x4=m;
y4=n;
}
}
//判断鼠标是按左键还是右键
if(e.getButton()==1)
{ //按左键,颜色为sd得到的黑色
g.setColor(sd.getColor());
}
else if(e.getButton()==3)
{ //按右键,颜色为绿色
g.setColor(Color.GREEN);
}
}
//鼠标释放时的坐标
public void mouseReleased(MouseEvent e)
{
//记录第二次点击时的坐标
x2=e.getX();
y2=e.getY();
//判断是否为Line
if(sd.getStr().equals("Line"))
{
// //调用画布对象的方法,画直线
// g.drawLine(x1,y1,x2,y2);
Shape shapeLine=new ShapeLine(x1,x2,y1,y2,Color.BLACK,1);
shapeLine.draw(g);
list.add(shapeLine);//将直线类添加到队列上
}
/*
* 判断是否为矩形Rectangle
*/
if(sd.getStr().equals("Rectangle"))
{
// g.drawRect(Math.min(x1, x2), Math.min(y1, y2),Math.abs(x1-x2), Math.abs(y1-y2));//Math.min(x1, x2), Math.min(y1, y2)!
Shape shapeRectangle=new ShapeRectangle(x1,x2,y1,y2,Color.BLACK,1,4);
shapeRectangle.draw(g);
list.add(shapeRectangle);//将矩形类添加到队列上
}
/*
* 判断是否为椭圆形Oval
*/
if(sd.getStr().equals("Oval"))
{
// g.drawOval(x1, y1,Math.abs(x1-x2) , Math.abs(y1-y2));
Shape shapeOval=new ShapeOval(x1,x2,y1,y2,Color.BLACK,1,4);
shapeOval.draw(g);
list.add(shapeOval);//将椭圆类添加到队列上
}
/*
* 判断是否为RoundRect
*/
if(sd.getStr().equals("RoundRect"))
{
// g.drawRoundRect(x1, y1, Math.abs(x1-x2),Math.abs(y1-y2), Math.abs(x1-x2)/2,Math.abs(y1-y2)/2 );
Shape shapeRoundRect=new ShapeRoundRect(x1,x2,y1,y2,Color.BLACK,1,4);
shapeRoundRect.draw(g);
list.add(shapeRoundRect);
}
/*
* 判断是否为Polygon
*/
//获取拖动过程中的坐标值
int x = e.getX();
int y = e.getY();
if(sd.getStr().equals("Polygon"))
{
//判断鼠标点击的坐标是不是与第一次点击的一样,不同则画线
if((x!=m)||(y!=n))
{
// //画线
// g.drawLine(x4, y4, x, y);
Shape shapePolygon=new ShapePolygon(x4,x,y4,y,Color.BLACK,1,4);
shapePolygon.draw(g);
list.add(shapePolygon);
//赋值
x4=x;
y4=y;
}
// Shape shapePolygon=new ShapePolygon(x4,y4,x,y,Color.BLACK,1,4);
// shapePolygon.draw(g);
// list.add(shapePolygon);
}
}
/*
* 当鼠标在事件源上单击时按键执行的方法
*/
public void mouseClicked(MouseEvent e)
{
if(sd.getStr().equals("Polygon"))
{
//获取点击过程中的坐标值
int x = e.getX();
int y = e.getY();
//如果双击鼠标,当前的点会与初始点连线
if(x==m1&&y==n1)
{
// g.drawLine(x, y, m, n);
Shape shapePolygon=new ShapePolygon(x,m,y,n,Color.BLACK,1,4);
shapePolygon.draw(g);
list.add(shapePolygon);
flag=1;//使flag归为1,便能改变再次画多边形时的第一次单击的坐标值
}
m1=e.getX();
n1=e.getY();
}
}
public void mouseEntered(MouseEvent e)
{}
public void mouseExited(MouseEvent e)
{}
/*
* 当鼠标在事件源上拖拉时按键执行的方法
*/
public void mouseDragged(MouseEvent e) {
//判断点击的按钮是否为"Pencil"
if(sd.getStr().equals("Pencil"))
{
//获取x,y的坐标值
int x=e.getX();
int y=e.getY();
// //画线
// g.drawLine(x1, y1, x, y);
Shape shapePencil=new ShapePencil(x1,x,y1,y, Color.BLACK, 1, 4);
shapePencil.draw(g);
list.add(shapePencil);
//赋值
x1=x;
y1=y;
}
///
//判读点击的按钮是否为"Eraser"
if(sd.getStr().equals("Eraser"))
{
//获取x,y的坐标值
int x=e.getX();
int y=e.getY();
// //设置线条为白色
// g.setColor(Color.WHITE);
// //画线
// gg =(Graphics2D)g; //实例化一个Graphics2D对象
// //设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND
// gg.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
// gg.drawLine(x1,y1,x,y);
// gg.setStroke(new BasicStroke());
Shape shapeEraser=new ShapeEraser(x1,x,y1,y,Color.WHITE,1,4);
shapeEraser.draw(g);
list.add(shapeEraser);
//赋值
x1=x;
y1=y;
}
///
//判读点击的按钮是否为"Brush"
if(sd.getStr().equals("Brush"))
{
//获取x,y的坐标值
int x=e.getX();
int y=e.getY();
// //设置线条为选择的颜色
// g.setColor(sd.getColor());
// //画线
// gg =(Graphics2D)g; //实例化一个Graphics2D对象
// //设置笔的粗细为2.0F,线条端点是CAP_ROUND,点划线模式为 JOIN_ROUND
// gg.setStroke(new BasicStroke(10.0F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
// gg.drawLine(x1,y1,x,y);
// gg.setStroke(new BasicStroke());
Shape shapeBrush=new ShapeBrush(x1,x,y1,y,Color.BLACK,1,4);
shapeBrush.draw(g);
list.add(shapeBrush);
//赋值
x1=x;
y1=y;
}
//判读点击的按钮是否为"Gunjet"
if(sd.getStr().equals("Gunjet"))
{
//获取x,y的坐标值
int x=e.getX();
int y=e.getY();
int x0=0;
int y0=0;
// //设置线条为黑色
// g.setColor(sd.getColor());
// //噴槍大小
// int size=8;
// //噴槍点數
// int count=10;
// for(int i=0;i<count;i++)
// { x0 = new Random().nextInt(size)+ 1;
// y0 = new Random().nextInt(size) + 1;
// g.fillRect(x+x0, y+y0,5,5);
// }
Shape shapeGunjet=new ShapeGunjet(x1,x,y1,y,Color.BLACK,1,4);
shapeGunjet.draw(g);
list.add(shapeGunjet);
}
}
/*
* 事件源上移动鼠标的方法
*/
public void mouseMoved(MouseEvent e) {
}
}
这样就基本完成啦,然后至于对界面的美好等就需要继续努力咯!!