一、接口
接口是一种极度抽象的类,在软件工程中,接口泛指别人调用的方法和函数。接口定义的关键字为interface,定义方法为:
interface 接口名{};
接口中可以定义属性和方法。但接口中的属性会被隐性的定义为public static final属性(并且只能是public属性),接口中的方法会被隐性的定义为public abstract(只能是public方法),即接口中的方法一定是抽象方法。接口中一般不定义属性。
我们可以写一个Shape接口:
import java.awt.Graphics;
public interface Shape{
//图形绘制
public void draw(Graphics g);
//擦去图形
public void wipe(Graphics g);
}
接口需要特别注意:
1、接口的方法必须是抽象方法,不能实现。
2、接口不能直接定义对象,但它可以被普通类实现,这时需要用到关键字implements,具体格式:
public class 类名 implements 接口名{};
3、实现接口的普通类必须重写接口中的所有方法,同时一个普通类可以同时实现多个接口。
4、接口没有构造方法。
二、抽象类
抽象类:如果说接口是极度抽象的类,那么抽象类就是比较抽象的类。抽象类和接口最显而易见的区别就是抽象类的方法是可以在抽象类中实现的。抽象类使用到的关键字是abstract,定义格式是:
public abstract class 抽象类名
我们写一个Shape抽象类:
import java.awt.Graphics;
public abstract class Shape{
//图形绘制
public void draw(Graphics g){
g.drawLine(1,1,100,100);
};
//擦去图形
public void wipe(Graphics g);
}
抽象类同样不能直接定义对象,它可以由普通类去实现,实现关键字和继承一样是extends。同时一个类只能继承一个抽象类。
三、图形的重绘
我们在制作画图板的时候,会出现这样一种情况,当我们拖动界面到边缘被遮住或者最小化、最大化界面的时候,之前我们在画图板上画出的图形就会不见。这是什么原因?这必须说到JFrame类和Jpanel当中的paint方法。paint方法是用来绘制图形的,当调用paint方法的时候,图形就会开始绘制。拖动界面,最小化、最大化界面等行为我们都可以称为是在改变图形状态。每当我们改变图形状态的时候,paint方法会被重新调用,也就是重新绘制图形,而之前我们绘制的图形没有保存,这就造成了图形状态改变时,画图板上的图形就消失了。
所以,图形的重绘就是保存状态改变前的图形,在图形状态改变后将它绘制出来。所以重绘有两步:
1、保存状态改变前的图形
2、将图形绘制出来
那该如何做呢?
1、我们首先要保存状态改变前的图形,那这个图形毫无疑问可以重新创建一个类来保存,因考虑到图形可能会很多,所以我们用一个类数组来保存多个图形。
2、图形保存好后,绘制我们首先想到的就是重写paint方法,因为每次图形状态改变就会调用paint方法,我们只要在paint方法里绘制状态改变前的图形就行了。
重绘的基本方法:
1、创建一个Shape类,保存图形。Shape有一个构造方法,能将该图形的相关参数传入,同时还有一个绘制图形的方法。
public class Shape{
public int x1,x2,y1,y2;
Color color; //颜色
String type; //绘制的图形类型
public Shape(int x1,int y1,int x2,int y2,String type,Color color){
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.type = type;
this.color =color;
}
//图形绘制
public void paint(Graphics g){
g.setColor(color);
if("直线".equals(type))
g.drawLine(x1, y1, x2, y2); //重绘直线
if("圆".equals(type))
g.drawOval(x1, y1, x2-x1, y2-y1); //重绘圆
if("长方形".equals(type))
g.drawRect(x1, y1, x2-x1, y2-y1); //重绘长方形
}
}
2、我们需要在监听器里将绘制好的图形保存到shape类数组中(我们举一个直线例子)。
public int x1,y1,x2,y2,x3,y3;
private Graphics g;
Shape[] data = new Shape[1000]; //保存图形的数组
int dataCount = 0; //记录保存图形个数
public void mouseDragged(MouseEvent e) {
//获取x,y坐标
x2 = e.getX();
y2 = e.getY();
if(frontColor == null)
frontColor = frontColor.black;
if(shape == "直线"){
g.setColor(color);
g.drawLine(x1, y1, x3, y3); //x3是上一次拖动的坐标
g.setColor(frontColor);
g.drawLine(x1, y1, x2, y2);
}
x3 = x2;
y3 = y2;
}
public void mouseReleased(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
Shape s = new Shape(x1,y1,x2,y2,shape,frontColor);
data[dataCount++] = s; //松开时将图形保存在data类数组中。
}
3、创建一个JFrame的子类DrawFrame和一个显示界面的类FrameUI,FrameUI将保存的图形数组传给DrawFrame,重写DrawFrame的paint方法,DrawFrame的paint方法中调用父类的paint类,即开始重绘主界面,然后调用Shape图形类当中绘制图形的方法。之后我们在显示界面的类FrameUI中定义一个显示界面的方法,在该方法内创建DrawFrame的对象,开始绘制界面。
public class FrameUI {
public void showUI(){
DrawFrame DrawFrame = new DrawFrame();
DrawFrame.setSize(1000, 500);
DrawFrame.setLocationRelativeTo(null); //界面居中
DrawFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawFrame.setLayout(new FlowLayout()); //流式布局
Shape[] shape;
Color color = DrawFrame.getBackground(); //获取背景色
UIlistener FrameLis = new UIlistener(color);
DrawFrame.addMouseMotionListener(FrameLis); //添加鼠标拖动移动监听器
DrawFrame.addMouseListener(FrameLis); //添加鼠标监听器
String[] shapebtn = {"长方形","圆形","直线","多边形"};
for(int i=0 ; i<shapebtn.length ; i++){ //图形按钮
JButton btn = new JButton(shapebtn[i]);
btn.setSize(25, 25);
DrawFrame.add(btn); //添加按钮
btn.addActionListener(FrameLis); //添加按键监听器
}
Color[] colbtn = {Color.black,Color.red,Color.green,Color.blue}; //颜色数组
for(int i=0 ; i<colbtn.length ; i++){ // 颜色按钮
JButton btn = new JButton();
btn.setSize(25, 25);
btn.setBackground(colbtn[i]); //设置按钮背景色
DrawFrame.add(btn); //添加按钮
btn.addActionListener(FrameLis); //添加按键监听器
}
//设置窗体可见
DrawFrame.setVisible(true);
Graphics g = DrawFrame.getGraphics();
FrameLis.setg(g); //传入画笔
DrawFrame.shape = FrameLis.data; //将监听器中保存的图形数组传递给DrawFrame
}
}
public class DrawFrame extends JFrame{
Shape[] shape;
public void paint(Graphics g){
super.paint(g);
for(int i=0;i<shape.length;i++)
shape[i].paint(g);
}
}
四、在重绘中加入接口
状态改变前保存的图形因为可能存在多种类型,如直线,圆,矩形等等,所有图形又存在绘制的方法,但绘制方法又各不相同。由此,我们可以想到接口,我们可以定义一个图形接口,然后每种图形创建一个类实现该接口。
我们更改上述的Shape类:
public interface Shape{
//图形绘制
public void draw(Graphics g);
}
创建各种图形类:
public class Line implements Shape{
public int x1,x2,y1,y2;
public Line(int x1,int y1,int x2,int y2){
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
public void draw(Graphics g) {
g.drawLine(x1, y1, x2, y2);
}
}
public class Oval implements Shape{
int x,y,width,height;
public Oval(int x,int y,int width,int height){
this.x = x;
this.y = y;
this.width =width;
this.height = height;
}
public void draw(Graphics g) {
g.drawOval(x, y, width, height);
}
}
public class Rect implements Shape{
int x,y,width,height;
public Rect(int x,int y,int width,int height){
this.x =x;
this.y = y;
this.width =width;
this.height = height;
}
public void draw(Graphics g) {
g.drawRect(x, y, width, height);
}
}
该种方法虽然看似麻烦,但我们每次添加一种图形的时候只需要加一个图形类,然后对监听器中的图形保存稍作更改即可,而其他代码可以保持不变,事实上加入继承机制后的重绘要方便许多!