资源描述
目 录
1 课题介绍 1
1.1 课程设计目的 1
1.2 课程设计要求 1
1.3 课程设计主要知识点 1
2 总体设计 2
2.1 画板界面设计图 2
2.2 模块概述 2
2.2.1 文件操作 2
2.2.2 图形绘制 2
3 详细设计与实现 4
3.1 框架类DrawGraphic 4
3.1.1菜单 4
3.1.2工具栏 6
3.1.3画图区域 7
3.2 基本图形类 10
3.2.1 父类Drawings 10
3.2.2 子类(只列出部分) 10
4 设计中的难点 12
4.1多态与动态绑定 12
4.2重绘的理解 12
4.3文件的新建、打开和保存 12
5 运行测试 13
6 总结 15
参考文献 16
模拟画图16
1 课题介绍
1.1 课程设计目的
课程设计题目:模拟画图
要求设计一个程序,模拟Windows中的画图程序,实现图形图像的编辑,修改,保存等功能。
1.2 课程设计要求
(1)程序中必须包括“新建”、“打开”、“保存”,用户可以选择,实现对文件的操作;
(2) 画图板的绘图按钮用来画出不同的图形,包括实心图形和空心的图形;
(3)可以对线条的颜色和粗细进行设置,也可以对填充图形的颜色进行设置和更改。
1.3 课程设计主要知识点
(1) 框架类DrawGraphic继承了Swing类库中JFrame,并且用到JButton,JLabel,JPanel,等Swing类库中的组件进行窗体设计;
(2)选择一个文件,用到组件JFileChooser;
(3)选择颜色,用到组件JColorChooser;
(4)弹出标准对话框,用到组件JOptionPane;
(5)在单击绘图按钮时,所触发的动作通过方法addActionListener和ActionListener接口的actionPerformed方法共同实现;
(6)MouseAdapter和MouseMotionAdapter用来完成鼠标各种事件的相应操作,包括单击、移动、拖拽等;
(7)在界面设计的时候结合BorderLayout和GridLayout两种布局格式;
(8)在添加监听器的时候有用到内部匿名类和内部类,所以要熟悉它们的构造方法和使用方法,从中体会持有对方引用的这一设计思想。
(9)在图形绘制的时候,用到Graphics类,其揭示了所有图形系统几乎都采用的一种机制,就是如何在窗口上画出一些图形来,当窗口构造出来的时候里面有一支画笔,即Graphics的一个对象,当窗口调用paint方法的时候,系统会把画笔自动传递给它,拿到画笔,重写paint方法就实现了在窗口上绘制基本图形。
2 总体设计
2.1 画板界面设计图
定义框架类DrawGraphic,然后在框架上直接添加菜单栏,整个框架采用 BorderLayout的布局格式。工具栏、显示鼠标位置的标签、主要的画图区域分别在框架的west,south,center三块区域。而浮动式工具栏采用的是GridLayout布局格式,其初始方向设定为VERTICAL,如下图所示:
2.2 模块概述
2.2.1 文件操作
文件的新建,打开,保存可以添加内部匿名类的方式实现,new一个事件监听器ActionListener,里面调用actionPerformed的方法,被监听的按钮一旦被触发就调用函数执行相应的操作。
2.2.2 图形绘制
在工具栏里面实现了基本图形的绘制,图形属性的设置,如画笔粗细和画笔颜色的设置,以及橡皮擦,文字输入等功能,而这些功能的实现都是通过添加内部事件监听器类来实现的。一个类用来监听绘制基本图形以及橡皮擦按钮;另一个类用来监听的是选择颜色按钮、选择画笔粗细按钮、和输入文字按钮,里面都用if语句和e.getSource来判断事件源,从而在触发时,调用不同的函数,当事件源为输入文字时,用JOptionPane.showMessageDialog来弹出一个提示操作的对话框。
画图区域的功能主要是通过添加鼠标监听器来实现的:
一个鼠标监听器监听的是:单击鼠标,释放鼠标,鼠标进入绘图区域,鼠标离开绘图区域这四个鼠标的动作事件;
另一个监听的是:鼠标拖拽和鼠标移动。而且,两个监听器之间是存在着密不可分的关系的,它们同时监听画图区域。
铅笔作画和橡皮擦的使用是画图板设计的核心也是难点,而且两者的实现原理是一样的,我们通常画图的时候,一定是先单击鼠标然后拖拽鼠标最后释放鼠标的,所以,在画图的过程中,只要鼠标单击一下就获得(x1,y1),紧接着用if语句判断画的基本图形是哪一个,如果是铅笔或者橡皮擦,则获得(x2,y2),说明在铅笔或橡皮擦的时候,鼠标单击一下,就获得一个点,x1=x2,y1=y2,且这个点的坐标就是鼠标单击的位置,而且这个点是算作第一个基本图形的,此时index=1;拖拽的过程中动态获得鼠标所在位置的横纵坐标且始终x1=x2,y1=y2,并且等于第一个基本图形也就是index=1的那个点的x2,y2,即,在铅笔作画的过程中,在鼠标不断拖拽的过程中,index=1时的那个点在以点的点的长度不断增加,这就是铅笔作画过程的实现;鼠标释放的时候,在铅笔或橡皮的状态下,也是得到一个点。所以,可以总结,铅笔和橡皮都是通过设置画直线方法中的点的坐标相等来实现的。其它图形的绘制,可以直接调用Graphics2D中的方法实现,相对比较简单。
3 详细设计与实现
3.1 框架类DrawGraphic
3.1.1菜单
菜单栏有两个按钮“画图板”、“帮助”,通过添加内部匿名类来实现,一旦下拉菜单中的“新建”、“打开”、“保存”、“退出”四个键被触发,就调用相关的函数,具体代码如下:
(1)“新建”执行时,调用的函数代码如下:
public void newFile(){
index = 0;
currentChoice = 3;
color = Color.BLUE;
stroke = 1.0f;
createNewItem();
repaint();
}
(2)“打开”执行时,调用的函数代码如下:
public void openFile(){
JFileChooser fileChooser = new JFileChooser();//为用户选择文件提供了一种简单的机制
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int result = fileChooser.showOpenDialog(this);//弹出一个 "Open File" 文件选择器对话框,父组件
if(result == JFileChooser.CANCEL_OPTION) return;
File fileName = fileChooser.getSelectedFile();
fileName.canRead();//测试应用程序是否可以读取此抽象路径名表示的文件
if(fileName == null||fileName.getName().equals("")){
JOptionPane.showMessageDialog(fileChooser, "这个名字不可以用的"," ",
JOptionPane.ERROR_MESSAGE);
}
else{
try{
FileInputStream fis = new FileInputStream(fileName);
input = new ObjectInputStream(fis);
int countNumber=0;
countNumber = input.readInt();
for(index=0;index<countNumber;index++){
Drawings inputRecord = (Drawings)input.readObject();
itemList[index] = inputRecord;
}
createNewItem();
input.close();
repaint();
}catch(EOFException endofFileException){//当输入过程中意外到达文件或流的末尾时,抛出此异常
JOptionPane.showMessageDialog(this, "no more record in file",
"end of file",JOptionPane.ERROR_MESSAGE);
}catch(ClassNotFoundException classNotFoundException){
JOptionPane.showMessageDialog(this, "unable to create object",
"class not found",JOptionPane.ERROR_MESSAGE);
}catch(IOException ioException){
JOptionPane.showMessageDialog(this, "error during read from file",
"read error",JOptionPane.ERROR_MESSAGE);
}
}
}
(3)“保存”执行时,调用的代码如下:
public void saveFile(){
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); //允许用户只选择文件
int result = fileChooser.showSaveDialog(this);//弹出一个 "Save File" 文件选择器对话框
if(result == JFileChooser.CANCEL_OPTION) return;
File fileName = fileChooser.getSelectedFile();
fileName.canWrite();
if(fileName == null||fileName.getName().equals("")){
JOptionPane.showMessageDialog(fileChooser, "这个名字不可以用的"," ",JOptionPane.ERROR_MESSAGE);
}
else{
try{
fileName.delete();
FileOutputStream fos = new FileOutputStream(fileName);
output = new ObjectOutputStream(fos);
output.writeInt(index); //写入一个32位的int值
for(int i= 0;i<index;i++){
Drawings outputRecord = itemList[i];
output.writeObject(outputRecord); //将指定的对象写入 ObjectOutputStream
output.flush(); //此操作将写入所有已缓冲的输出字节,并将它们刷新到底层流中
}
output.close();
fos.close();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
}
3.1.2工具栏
工具栏按钮功能的实现,是通过添加两个内部事件监听类ButtonHandler1、ButtonHandler2来达到目的的。
(1)ButtonHandler1监听绘制基本图形的按钮,if语句判断事件源,具体代码如下:
public class ButtonHandler1 implements ActionListener{
public void actionPerformed(ActionEvent e){
for(int j=3;j<choices.length-3;j++){
if(e.getSource()==choices[j]){
currentChoice = j;
createNewItem();
repaint();
}
}
}
}
(2)ButtonHandler2监听颜色选色器、画笔粗细、添加文字按钮,if语句判断事件源,相应的按钮被触发,就执行相应的函数,具体代码如下:
public class ButtonHandler2 implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource()==choices[choices.length-3]){
chooseColor();
}
if(e.getSource()==choices[choices.length-2]){
setStroke();
}
icon = new ImageIcon(getClass().getResource("/images/smile.png"));
if(e.getSource()==choices[choices.length-1]){
JOptionPane.showMessageDialog(null, "想在哪里添加文字呢?鼠标先点一下那里吧!",
" ",JOptionPane.INFORMATION_MESSAGE,icon);
currentChoice = 14;
createNewItem();
repaint();
}
}
}
3.1.3画图区域
(1)createNewItem()用来new各种基本图形,在函数的一开始,我设置了一下光标的样子,然后用switch函数接收currentChoice来判断用户要new的是哪一个基本图形,关键代码如下:
void createNewItem(){
if(currentChoice == 14){
drawingArea.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
}
else{
drawingArea.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
switch(currentChoice){
case 3:itemList[index] = new Pencil();break;
case 4:itemList[index] = new Line();break;
case 5:itemList[index] = new Rect();break;
case 6:itemList[index] = new fillRect();break;
case 7:itemList[index] = new Oval();break;
case 8:itemList[index] = new fillOval();break;
case 9:itemList[index] = new Circle();break;
case 10:itemList[index] = new fillCircle();break;
case 11:itemList[index] = new RoundRect();break;
case 12:itemList[index] = new fillRoundRect();break;
case 13:itemList[index] = new Rubber();break;
case 14:itemList[index] = new Word();break;
}
itemList[index].type = currentChoice;
itemList[index].R = R;
itemList[index].G = G;
itemList[index].B = B;
itemList[index].stroke = stroke;
}
(2)chooseColor()用来选择各种颜色,具体代码如下:
public void chooseColor(){
color = JColorChooser.showDialog(null, "在这里选择自己喜欢的颜色", color);
R = color.getRed();
G = color.getGreen();
B = color.getBlue();
}
(3)setStroke()用来设置画笔粗细,具体代码如下:
public void setStroke(){
String input;
input = JOptionPane.showInputDialog(null,"在这里重新输入画笔的粗细值(一个>0的实数)","1.0");
stroke = Float.parseFloat(input); //将字符串参数转换为一个 float 值
itemList[index].stroke = stroke;
}
(4)mouseA监听鼠标单击、释放、进入、离开四个事件,当鼠标单击的时候,用e.getSource()先得到一个点的坐标初始化(x1,y1),再判断单击的是哪一个按钮,如果是绘制其它图形的按钮,那就光得到一个点(x1,y1);如果是铅笔或者橡皮擦,则继续初始化第二个点的坐标(x2,y2),此时x1=x2,y1=y2,即鼠标单击绘出一个点,而这个点是用绘直线方式画出的,表示已绘制的图形个数的index++;如果是添加文字按钮,则弹出一个可以输入文字的对话框。当鼠标释放的时候,如果是铅笔或橡皮,同样得到一个点,如果是绘制其它图形的基本按钮,那就光得到(x2,y2),关键代码如下:
class mouseA extends MouseAdapter{
public void mousePressed(MouseEvent e){
statusBar.setText("Mouse Pressed@:["+e.getX()+","+e.getY()+"]");
itemList[index].x1 =e.getX();
itemList[index].y1 =e.getY();
if(currentChoice == 3||currentChoice == 13){
itemList[index].x2= e.getX();
itemList[index].y2= e.getY();
index++;
createNewItem();
repaint();
}
if(currentChoice == 14){
itemList[index].x1 = e.getX();
itemList[index].y1 = e.getY();
String input;
input = JOptionPane.showInputDialog("可以添加你想写的文字咯!");//可以输入内容的对话框
itemList[index].s1 = input;
itemList[index].x2 = f1;
itemList[index].y2 = f2; //f1,f2用来存放当前字体风格
itemList[index].s2 = stylel;
index++;
currentChoice = 14;
createNewItem();
drawingArea.repaint();
}
}
public void mouseReleased(MouseEvent e){
statusBar.setText("Mouse Released@:["+e.getX()+","+e.getY()+"]");
if(currentChoice == 3||currentChoice == 13){
itemList[index].x1 = e.getX();
itemList[index].y1 = e.getY();
}
itemList[index].x2 = e.getX();
itemList[index].y2 = e.getY();
index++;
createNewItem();
repaint();
}
}
(5)mouseB监听鼠标拖拽和移动,在鼠标拖拽的时候,由于鼠标的拖拽一定是在单击之后的,所以mouseA和mouseB的监听功能必须连在一起来分析,上面得知,如果事件源是铅笔或橡皮,鼠标单击就得到了第一个点,紧接着鼠标开始拖拽,拖拽的过程中,也一直在动态的得到点,只是第一个点的x2,y2一直在以点的长度动态增加,index++;如果事件源是绘制其它图形的按钮,则鼠标在动态拖拽的过程中只需要,得到点(x2,y2)就好:
class mouseB extends MouseMotionAdapter{
public void mouseDragged(MouseEvent e){
statusBar.setText("Mouse Dragged@:["+e.getX()+","+e.getY()+"]");
if(currentChoice == 3||currentChoice == 13){
itemList[index-1].x2= itemList[index].x2= itemList[index].x1=e.getX();
itemList[index-1].y2= itemList[index].y2= itemList[index].y1=e.getY();
index++;
createNewItem();
}
else{
itemList[index].x2 = e.getX();
itemList[index].y2 = e.getY();
}
repaint();
}
public void mouseMoved(MouseEvent e){
statusBar.setText("Mouse Moved@:["+e.getX()+","+e.getY()+"]");
}
}
3.2 基本图形类
3.2.1 父类Drawings
class Drawings implements Serializable{
int x1,y1,x2,y2;
int R,G,B;
float stroke;
int type;
String s1,s2;
void draw(Graphics2D g2d){};
}
3.2.2 子类(只列出部分)
(1)铅笔:
class Pencil extends Drawings{
void draw(Graphics2D g2d){
g2d.setPaint(new Color(R,G,B));
g2d.setStroke(new BasicStroke(stroke));
g2d.drawLine(x1, y1, x2, y2);
}
}
(2)空心圆
class Circle extends Drawings{
void draw(Graphics2D g2d){
g2d.setPaint(new Color(R,G,B));
g2d.setStroke(new BasicStroke(stroke));
g2d.drawOval(Math.min(x1, x2),Math.min(y1, y2),
Math.max(Math.abs(x1-x2),Math.abs(y1-y2)),Math.max(Math.abs(x1-x2),Math.abs(y1-y2)));
}
}
(3)实心圆
class fillCircle extends Drawings{
void draw(Graphics2D g2d){
g2d.setPaint(new Color(R,G,B));
g2d.setStroke(new BasicStroke(stroke));
g2d.fillOval(Math.min(x1, x2),Math.min(y1, y2),
Math.max(Math.abs(x1-x2),Math.abs(y1-y2)),Math.max(Math.abs(x1-x2),Math.abs(y1-y2)));
}
}
(4)橡皮擦:
class Rubber extends Drawings{
void draw(Graphics2D g2d){
g2d.setPaint(new Color(255,255,255));
g2d.setStroke(new BasicStroke(stroke+7));
g2d.drawLine(x1, y1, x2, y2);
}
}
(5)文字
class Word extends Drawings{
void draw(Graphics2D g2d){
g2d.setPaint(new Color(R,G,B));
g2d.setFont(new Font(s2,x2+y2,((int)stroke)*30));
if(s1!=null)
g2d.drawString(s1, x1, y1);
}
}
4 设计中的难点
4.1多态与动态绑定
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。
动态绑定:在运行时根据具体对象的类型进行绑定。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断实际对象的类型,并分别调用相应的方法。JAVA中的多态就是借助动态绑定来实现的。
先定义一个父类Drawings实现接口Serializable,里面定义一个draw(),然后所有的基本图形类都从父类继承,作为子类,它们都重写了父类的draw方法,但是每个draw()都不一样,在绘制任何形状的时候,只需要简单的调用父类的draw()即可,然后让程序动态的根据对象的类型,决定使用哪个子类的draw方法,为了使画出的图形更加的美观,我在draw方法中使用了Graphics2D对象的引用。这样,有了多态的存在,就可以在不改变原程序的情况下,对其可以画出的图形进行扩展,丰富程序的功能。
4.2重绘的理解
当一个窗口被构造出来的时候,里面就有一支画笔,在这个窗口出现再次显示、或大小改变等情况的时候,窗口就需要被重画,只要窗口需要被重画,paint就会被自动调用。repaint的作用是让窗口进行强制重画,其内部调用了paint方法,所以整个窗口在调用repaint的时候,也会被重画。
从而,我们可以理解图形系统共同采用的机制,它里面一定会有一个类似repaint()的方法,在窗口调用这个方法的时候,整个屏幕进行重画,而这个方法的调用过程一定是分成两步的,先调用update(),然后再调用paint()。画笔在我们new出来的窗口里面,repaint()里面没有画笔,update()和paint()里面有画笔,如果没有找到窗口里的画笔,不可以直接调用paint()。
4.3文件的新建、打开和保存
JAVA把每个文件都视为顺序字节流,如果在JAVA中要用到文件处理,必须用到java.io包,这个包定义了一些流类,选用FileInputStream从文件中读入数据,选用FileOutputStream向文件中输出数据,通过建立这些文件对象变可以打开文件。如果想把一个对象序列化成一个字节流,写到硬盘上,那么就必须实现接口Serializable,这是一个标记性的接口,给实现它的类打了一个标记,给编译器看的,知道这个类可以被序列化。
5 运行测试
程序运行后,按“打开”可有如下界面:
图5-1 模拟画图器界面
图5-2 铅笔自由作画
图5-3 基本图形绘制
6 总结
对于这次JAVA课程设计,我认为自己设计的模拟画图器有一些成功的地方,也有很多问题需要通过以后的学习不断寻求解决的方案。
先说自己有成就感的地方,这是我第一个用图形界面设计出来的、有实用性的程序,虽然看起来很简单,可实现起来并不容易,可以说是困难重重,我开始的时候是无从下手的,通过上网查资料,去学校的图书馆借阅相关书籍,才实现了现在的这些功能,即可以在画板上面实现自由作画,画出一些基本的图形,而且界面上的一些基本图形的小图标都是我用小图标制作软件做出来的,虽然不是很美观的,但至少是独一无二的。
其实,这次的课程设计更多的是让我看到了自己的不足,在设计之前,我构想了很多的功能,比如说,撤销重做等,或把画图时常用的一些功能键都添加在工具栏当中,但是由于自己的尤其是在自己测试的过程中,发现和其它的画图软件相比,我仍有很多功能需要实现比如说撤销重做的功能,虽然查阅了很多的资 料,但还是没能够添加到程序当中来。还有,我觉得自己程序的先有实现当中,存在一个最大的问题,就是预先分配好了在画板中可绘制的图形个数,所以一旦超出了这个范围,就会出现问题。
在设计的过程中,我对一些知识有了更深刻的理解,比如说多态,像我在程序设计的时候需要构造很多个不同的基本图形的类,就可以先构造一个基本图形的父类,让那些直线、圆,矩形等都从它去继承,使程序的可扩展性增强。
总之,我会不断地努力学习JAVA这门语言,现在简单的课程设计只是一个开始,只有不断的动手去编写更多的程序,才可以在实践中获得经验。
参考文献
[1] 董小园.JAVA面向对象程序设计.北京:清华大学出版社,2011.
[2] 张跃平.JAVA实用教程.北京:电子工业出版社,2011
[3] 李钟尉,陈丹丹.JAVA开发实战1200例(第I卷).清华大学出版社,2011
[4] 李钟尉,陈丹丹. JAVA开发实战1200例(第II卷).清华大学出版社,2011
展开阅读全文