设计画图板,主要有几个方向:Java.swing中的组件,监听器,队列的使用,文件的保存。
一. 画板界面
要设计一个画图板,首先需要窗体也就是画板界面(这里包括界面的一些属性)以及配套需要的窗体组件,如Jbutton, ButtonGroup,JradioButton.
- package draw;
- import java.awt.FlowLayout;
- import java.awt.Graphics;
- import javax.swing.JFrame;
- /**
- *画板窗体类
- * @author Administrator
- *
- */
- public class draws extends JFrame {
- /**
- * 程序入口
- *
- * @param args
- */
- public static void main(String[] args) {
- //创建一个draws窗体的对象
- draws ds = new draws();
- ds.dra();
- }
- /**
- * 显示画板窗体的方法
- */
- public void dra() {
- // 设置窗体的属性
- this.setTitle("简单画图板");//设置标题
- this.setBounds(100, 100, 500, 400);//设置大小
- this.setDefaultCloseOperation(3); //关闭时退出程序
- this.setLayout(new FlowLayout());//设置布局
- this.setLocationRelativeTo(null);
- this.setResizable(false);
- //定义两个按钮,保存和打开
- JButton button1=new JButton("保存");
- JButton button2=new JButton("打开");
- //定义两个面板
- JPanel p=new JPanel();
- JPanel p2=new JPanel();
- p2.add(button1);p2.add(button2);//将两个按钮添加到南面的面板上
- javax.swing.JButton btnColor = new javax.swing.JButton("颜色");
- this.add(btnColor); // 创建一个按钮组
- javax.swing.ButtonGroup group = new javax.swing.ButtonGroup();
- javax.swing.JRadioButton line = new javax.swing.JRadioButton("Line");
- line.setActionCommand("Line");
- line.setSelected(true);// 默认选中
- javax.swing.JRadioButton triangle= new javax.swing.JRadioButton("Triangle");
- triangle.setActionCommand("Triangle");
- javax.swing.JRadioButton rect = new javax.swing.JRadioButton("Rect");
- rect.setActionCommand("Rect");
- javax.swing.JRadioButton oval = new javax.swing.JRadioButton("Oval");
- oval.setActionCommand("Oval");
- //将四个按钮加入到组中
- group.add(line);
- group.add(triangle);
- group.add(rect);
- group.add(oval);
- p.add(b);//将颜色按钮添加到面板p上
- p.add(line); //将三个按钮添加到面板上
- p.add(triangle);
- p.add(rect);
- p.add(oval);
- p.setBackground(Color.gray);//设置面板的背景颜色
- p2.setBackground(Color.LIGHT_GRAY);
- this.add(p2,BorderLayout.SOUTH);
- this.add(p,BorderLayout.NORTH);//将面板添加到窗体上
- }
- }
以上是一个简单的画板界面。
二. 绘制图形
想要绘制的所有图形都有共同点,它们都称之为图形,都有颜色和画的方法。所以,可以定义一个图形的父类。
- package draw;
- import java.awt.Color;
- import java.awt.Graphics;
- /**
- * 形状抽象类,所有的形状都必须继承的类
- * @author Administrator
- *
- */
- public abstract class Shape {
- private Color c;//颜色
- /**
- *设置颜色的方法
- * @param c 得到颜色对象
- */
- public void setColor(Color c){
- this.c = c;
- }
- /**
- * 得到颜色的方法
- * @return 返回选中的颜色
- */
- public Color getColor(){
- return c;
- }
- /**
- * 绘制形状的抽象方法
- * @param g 画布对象
- */
- public abstract void draw(Graphics g);
- }
- 父类定义好了,接下来就是想要画的各种图形(只列出两种图形)
- package draw;
- import java.awt.Graphics;
- /**
- * 直线类,继承Shape
- * @author Administrator
- *
- */
- public class Line extends Shape{
- private int x1,y1,x2,y2;
- /**
- * 定义画直线的方法
- * @param x1
- * @param y1
- * @param x2
- * @param y2
- */
- public Line(int x1,int y1,int x2,int y2 ){
- this.x1 = x1;
- this.y1 = y1;
- this.x2 = x2;
- this.y2 = y2;
- }
- /**
- * 重写父类中的方法
- */
- public void draw(Graphics g){
- // /设置画布颜色
- g.setColor(this.getColor());
- g.drawLine(x1, y1, x2, y2);
- }
- }
- package draw;
- import java.awt.Graphics;
- /**
- * 定义一个椭圆形的子类,继承父类shape
- * @author Administrator
- *
- */
- public class Oval extends Shape{
- private int x1,y1,x2,y2;
- /**
- * 定义画椭圆的方法
- * @param x1 椭圆的边框坐标
- * @param y1
- * @param x2
- * @param y2
- */
- public Oval(int x1,int y1,int x2,int y2 ){
- this.x1 = x1;
- this.y1 = y1;
- this.x2 = x2;
- this.y2 = y2;
- }
- /**
- * 重写父类中的方法
- */
- public void draw(Graphics g){
- g.setColor(this.getColor());//设置颜色
- //绘制图形时,都是从左上角开始
- if(x1<x2&&y1<y2)
- g.drawOval(x1, y1, x2-x1, y2-y1);
- else if(x1<x2&&y1>y2)
- g.drawOval(x1, y2, x2-x1, y1-y2);
- else if(x1>x2&&y1<y2)
- g.drawOval(x2, y1, x1-x2, y2-y1);
- else
- g.drawOval(x2, y2, x1-x2, y1-y2);
- }
- }
三. 监听器
绘制的图形的方法可以了,我们就可以开始画图了。画图的过程就是,当鼠标点击并拖动到释放时,就能够画出图形来。所以就需要鼠标监听器,获得点击和释放时的坐标。这里为了简便画完之后就把它保存在自定义容器中。下面再说自定义队列。
- package draw;
- import java.awt.Graphics;
- import java.awt.event.MouseEvent;
- import java.awt.event.MouseListener;
- import javax.swing.ButtonGroup;
- public class drawL implements MouseListener{
- private int x1,y1,x2,y2;
- private Graphics g;
- private String s;
- private ButtonGroup group;
- private NList<Shape> shapes;
- /**
- * 构造方法
- * @param g 画布对象
- * @param group 选中的图形
- * @param shapes 自定义队列的容器
- */
- public drawL(Graphics g,ButtonGroup group,NList<Shape> shapes){
- this.g=g;
- this.group=group;
- this.shapes=shapes;
- }
- /**
- * 获得鼠标点击时的坐标
- */
- public void mousePressed(MouseEvent e){
- x1=e.getX();
- y1=e.getY();
- }
- /**
- * 获得鼠标释放时的坐标,并画出所需的图形
- */
- public void mouseReleased(MouseEvent e){
- x2=e.getX();
- y2=e.getY();
- //定义一个字符串用来表示获得的图形的名字
- String s=group.getSelection().getActionCommand();
- Shape sha=null;
- //根据选中的图形,类创建图形对象
- if("Line".equals(str)){
- sh = new Line(x1,y1,x2,y2);
- }
- if("Rect".equals(str)){
- sh = new Rect(x1,y1,x2,y2);
- }
- if("Oval".equals(str)){
- sh=new Oval(x1,x2,y1,y2);
- }
- if("Triangle".equals(str))
- sh=new Triangle(x1,y1,x2,y2);
- //图形的对象调用画的方法
- sha.draw(g);
- //将画过的形状对象保存到队列
- shapes.add(sha);
- }
- public void mouseEntered(MouseEvent e){}
- public void mouseExited(MouseEvent e){}
- public void mouseClicked(MouseEvent e){}
- }
四. 保存图形的自定义队列
- package draw;
- /**
- * 定义一个自定义队列的接口的实现类
- * @author Administrator
- *
- */
- public class NList<E> {
- /**
- * 定义一个添加元素的方法
- */
- public void add(E e){
- //定义一个新数组对象
- Object temp [] = new Object[this.stu.length+1];
- //循环将原始数组中的值拷贝到新的数组中
- for(int i=0;i<this.stu.length;i++){
- temp[i] = this.stu[i];
- }
- //将要添加的值,追加到新数组的末尾
- temp[this.stu.length] = e;
- //将新数组赋给原始数组
- this.stu = temp;
- }
- /**
- * 定义一个返回指定索引位值得方法
- */
- public E get(int index){
- return (E)stu[index];
- }
- /**
- * 定义一个返回值存储元素个数的方法
- */
- public int size(){
- return stu.length;
- }
- //定义一个数组
- private Object stu [] = new Object[0];
- }
五. 重绘,并且设置图形的颜色(这部分是我很纠结的地方)
在画的过程中,我们发现,我们已经画好的图形,只要被其他窗体覆盖,覆盖区的图形就都没有了。当我们把窗体缩小或放大时也出现类似情况。这主要的原因就是我们没有保存画好的图形。程序的变量都是放在内存的,而画出的图形是在缓存中。我们所要做的就是怎么使得这些图形保存到内存里。这就是重绘过程。
- //重写父类绘制窗体的方法
- public void paint(Graphics g){
- //调用父类的方法来绘制窗体
- super.paint(g);
- //将队列中的形状取出来绘制
- for(int i=0;i<shapes.size();i++){
- //根据下标取出一个形状对象
- Shape sh = shapes.get(i);
- //绘制
- sh.draw(g);
- }
- }
设置颜色,只是为了让图形更亮,更丰富些。
所以在窗体主类里,可以添加如下代码
- JButton b=new JButton("选颜色");//设置颜色按钮
- b.addActionListener(new ActionListener(){ //设置颜色按钮的事件监听器
- public void actionPerformed(ActionEvent e){
- color=JColorChooser.showDialog(null, "请选择颜色",Color.blue);
- lis.c=color;
- }
- });
- p.add(b);//添加到窗体上
这样当鼠标点击选择颜色按钮时,就会弹出一个颜色选择器,用户就可以在选择器上任一选择一种想要的颜色。
六.图形保存成文件
之前的重绘是将画好的图形保存到内存里,而这里是将画好的图形保存的硬盘上。(暂时还只是保存在指定的文件中)
对于所有的图形,可以知道,他们都有坐标,颜色,形状等特点。故 在保存时,就直接保存它们的这些特点就差不多了。
定义一个文件数据类
import java.awt.Color; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; public class datafile { /** * 将数据写入指定的文件中 */ public static void writefile(String path,NJListImp<Shape> shapes){ try{ FileOutputStream fos=new FileOutputStream(path); //定义一个文件输出流对象 DataOutputStream dos=new DataOutputStream(fos);//将文件流包装成数据类型流 //1.写入队列中的对象的个数 dos.writeInt(shapes.size()); System.out.println(shapes.size()); for(int i=0;i<shapes.size();i++){ Shape shape=shapes.get(i);//获取队列中 的一种形状 Color color=shape.getColor();//得到对象的颜色 int c=color.getRGB(); System.out.println(c); //2.写入队列中对象的颜色和类型 dos.writeInt(c); Byte type=shape.type; dos.writeByte(type); if(type==1){//如果图形时直线 Line line=(Line)shape;//将对象转化成直线 //分别获得直线的两坐标的数据 int x1=line.x1; int y1=line.y1; int x2=line.x2; int y2=line.y2; //3.写入坐标数据 dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); } else if(type==2){//如果是矩形 Rect rect=(Rect)shape; //分别获得矩形的对角两坐标的数据 int x1=rect.x1; int y1=rect.y1; int x2=rect.x2; int y2=rect.y2; //3.写入坐标数据 dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); System.out.println("--->>"); } else if(type==3){//如果是圆形 Oval oval=(Oval)shape; //分别获得圆的两坐标的数据 int x1=oval.x1; int y1=oval.y1; int x2=oval.x2; int y2=oval.y2; //3.写入坐标数据 dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); System.out.println("<<--->>"); } else{//如果是三角形 Triangle tra=(Triangle)shape; //分别获得它的三个坐标 int x1=tra.x1; int x2=tra.x2; int x3=tra.x3; int y1=tra.y1; int y2=tra.y2; int y3=tra.y3; //3.写入坐标数据 dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); dos.writeInt(x3); dos.writeInt(y3); } } dos.flush();//强制输出流 dos.close();//关闭输出流 } catch(Exception e){ e.printStackTrace(); } } /** * 读取文件中 的数据 * @param path 要读取 的文件的路径 * @return 将数据以shapes返回 */ public static NJListImp<Shape> readfile(String path){ NJListImp<Shape> shapes = new NJListImp<Shape>(); try{ //创建文件输入流 FileInputStream fis=new FileInputStream(path); //将文件输入流包装成基本数据类型流 DataInputStream dis=new DataInputStream (fis); //读取长度 int len=dis.readInt(); for(int i=0;i<len;i++){ int c=dis.readInt(); //读取颜色的rgb System.out.println(c); Color color=new Color(c);//通过RGB值得到颜色 byte type=dis.readByte();//读取图形的类型 if(type==1){//如果读取的是直线 int x1=dis.readInt();//连续读取四个int数据 int y1=dis.readInt(); int x2=dis.readInt(); int y2=dis.readInt(); Line line=new Line(x1,y1,x2,y2); line.type=type; line.setColor(color); // System.out.println(x1+"---"+y1+">>"+x2+"<<<"+y2); shapes.add(line); System.out.println(shapes.size()); } if(type==2){//如果读取的是矩形 int x1=dis.readInt();//连续读取四个int数据 int y1=dis.readInt(); int x2=dis.readInt(); int y2=dis.readInt(); Rect rect=new Rect(x1,y1,x2,y2); rect.type=type; rect.setColor(color); shapes.add(rect); } if(type==3){ //如果读取的是圆形 //连续读取四个int数据 int x1=dis.readInt(); int y1=dis.readInt(); int x2=dis.readInt(); int y2=dis.readInt(); Oval oval=new Oval(x1,y1,x2,y2); oval.type=type; oval.setColor(color);//设置图形的颜色 System.out.println(x1+"---"+y1+">>"+x2+"<<<"+y2); shapes.add(oval); } else if(type==0){ //如果是三角形 //连续读取六个int数据 int x1=dis.readInt(); int y1=dis.readInt(); int x2=dis.readInt(); int y2=dis.readInt(); int x3=dis.readInt(); int y3=dis.readInt(); Triangle tra=new Triangle(x1,y1,x2,y2); tra.setColor(color); tra.x3=x3; tra.y3=y3; shapes.add(tra); } } dis.close();//关闭输入流 } catch(Exception e){ e.printStackTrace(); } return shapes; } }
保存的方法可以了,接下来就是要实现它。也就是当点击“保存”和“打开”按钮时,能过实现功能。所有可以在这类里添加如下代码。
//匿名内部类做监听器
ActionListener al=new ActionListener(){
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("保存")){
datafile.writefile(path, shapes);
}
else {
shapes= datafile.readfile(path);
repaint();
System.out.println(path);
}
}
};
//分别给两个按钮添加监听器
button1.addActionListener(al);
button2.addActionListener(al);
以上就基本完成了这个简单的画图板了。感觉自己对文件的保存还很陌生,然后就是对于每个类之间的连接关系扯不清。特别是引用传参不明白。还需继续努力啊!