疯狂java实战演义第3章 图片浏览器.docx
《疯狂java实战演义第3章 图片浏览器.docx》由会员分享,可在线阅读,更多相关《疯狂java实战演义第3章 图片浏览器.docx(19页珍藏版)》请在冰豆网上搜索。
疯狂java实战演义第3章图片浏览器
第3章图片浏览器
3.1图片浏览器概述
相信使用Window操作系统的大多数用户,都使用过Windows的图片浏览器,或者是功能更强大与复杂的ACDSee图片浏览器(这个还支持编辑图片),图片浏览器最基本的功能是能浏览一个目录中的所有图片,并可以点击浏览上一张图片或者下一张图片,还有对图片放大与缩小,或者翻转图片等操作,在这里,实现了图片的浏览功能,导航功能(下一张、上一张),放大缩小功能。
本章将实现一个最简单的图片浏览器,包括了打开图片、放大与缩小图片、查看上一张和下一张图片等功能,图片浏览器的最终效果如图3.1所示。
图3.1图片浏览器
3.2创建图片浏览器的相关对象
我们首先需要创建图片浏览器的相关对象。
我们先创建图片浏览器的界面对象ViewerFrame,然后在该类中,我们为菜单、按钮加了事件监听器,所以定义了一个继承AbstractAction的类ViewerAction来响应这些动作。
在Action中响应动作,就到处理具体逻辑的步骤,我们把所有的逻辑处理放到ViewerService类中,ViewerService中包括打开图片、上一张、下一张、放大和缩小图片等功能,为了程序更好的解耦合,我们可以把具体的某些业务处理放置到独立的类中进行处理。
除了以上所说的几个类,由于我们这个程序有打开图片的操作,所以需要一个文件过滤器(只能选择图片类型的文件),所以定义了一个继承JFileChooser的类ViewChooser,这个类里面定义了自己的文件过滤器。
本章中所涉及的对象及它们之间的关系如图3.2所示。
图3.2图片浏览器类图
本章程序的功能较为简单,因此所涉及的对象也并不复杂,只有简单的五个对象。
3.2.1文件过滤器
如果要使文件对话框实现文件过滤功能,就需要结合FileFilter类来进行文件操作,文件过滤器是FileFilter的一个继承,也是文件对话框的内部类,里面重写了FileFilter的accept与getDescription方法:
❑booleanaccept(Filef),判断文件是否属于图片类型。
❑StringgetDescription(),获取过滤器的描述。
文件过滤器主要在用户打开图片时使用,当用户进行了图片选择后,就可以对用户所选择的文件进行验证。
当用户打开文件选择时,我们就可以对所有的文件进行一次过滤,文件选择器中只可以选择我们所定义的图片文件,那么其他的文件将不会被显示。
在本章中,文件过滤器是文件对话框类(ViewerFileChooser)的一个内部类(MyFileFilter)。
3.2.2文件对话框
Java文件对话框的实现比较简单,只要使用JFileChooser类并提供一个自己的构造器即可。
这里的文件对话框对象是JFileChooser类的子类,目的是为了加入在3.2.1中定义的文件过滤器:
❑voidaddFilter(),为这个文件对话框增加过滤器。
该对象中的addFilter方法主要用于向文件对话框加入文件过滤器,例如我们需要只显示.bmp的文件,那么可以在addFilter方法中使用以下代码实现:
this.addChoosableFileFilter(newMyFileFilter(newString[]{".BMP"},
"BMP(*.BMP)"));
在文件对话框的addFilter方法加入以上的代码后,那么用户将不能看到.bmp的文件,并且在“文件类型”的下拉中也只能选择.bmp,效果如图3.3所示。
在本章中,文件对话框对应的是ViewerFileChooser类。
图3.3文件过滤器的作用
3.2.3主界面类
我们建立一个界面类作为图片浏览器的主界面,该类包括图片显示区、菜单栏、工具栏,并为工具栏与菜单栏加上事件监听器,如下:
❑voidinit(),初始化图片浏览器的界面。
❑JLabelgetLabel(),获取显示图片的JLabel。
❑createToolPanel(),创建放大、缩小、上一张、下一张等工具按钮。
❑voidcreateMenuBar(),创建文件、工具、帮助等菜单。
在这里需要注意的是,由于打开的图片大小并不能确定,因此图片显示区必须使用JScrollPane。
在本章中,主界面对应的是ViewerFrame类。
3.2.4业务处理类ViewerService
业务处理类主要是处理图片浏览器的大部分业务逻辑,包括打开图片、关闭浏览器、放大图片、缩小图片、浏览上一张图片、浏览下一张图片等功能,如下:
❑staticViewerServicegetInstance(),获取ViewerService类的一个单态实例。
❑voidopen(ViewerFrameframe),弹出文件选择框,并读取被选择到的图片。
❑voidzoom(ViewerFrameframe,booleanisEnlarge),对正在浏览到的图片做放大或者缩小操作,这里可能会丢失图片精度。
❑voidlast(ViewerFrameframe),浏览上一张图片。
❑next(ViewerFrameframe),浏览下一张图片。
❑voidmenuDo(ViewerFrameframe,Stringcmd),响应菜单的动作。
在本章中,这个业务处理类并不是无状态的Java对象,也就是意味着本章的业务处理类将人保存一些业务状态,这些业务状态包括:
当前浏览的文件目录、文件目录的文件集合、图片放大或者缩小的比例等属性。
由于我们这个是有状态的Java对象,那么就意味着,如果访问的是同一个实例,那么该对象的这些属性将会被所有的访问者共享,如果其中的一个访问者改变了其中一个或者多个属性,那么其他的访问者将会受到影响。
当然,我们本章只是一个普通的图片浏览器,不存在多个用户使用同一个图片浏览器的情况。
在本章中,业务处理类对应的是ViewerService类。
3.2.5操作处理类
在本例中,由于用户可以执行的操作较少,因此,我们可以提供一个操作处理类来接收用户所有的操作,本例中的操作处理类是AbstractAction的一个子类,能用ImageIcon(图标)来创建一个Action,再用这个Action来创建按钮,点击按钮的时候,将调用此类的actionPerformed方法:
❑voidactionPerformed(ActionEvente),重写AbstractAction的方法,响应事件。
由于我们只有一个操作处理类,因此在实现actionPerformed方法时,我们就需要进行一系列的判断,让程序知道用户进行了何种操作,再调用业务处理类中的相应方法。
到此,图片浏览器的相关对象都已经建立,并且确定了我们需要实现哪些方法,我们在实现的过程中,如果发现可以对程序进行重构,那么也可以在重构的过程中,创建相关的类。
3.3创建主界面
这个图片浏览器的界面排版比较简单,只有菜单(不需要排版)、工具栏、图片显示区,我们使用BorderLayout进行布局,把工具栏放在BorderLayout.NORTH,把图片显示区放在BorderLayout.CENTER。
在本章中,由于打开图片的大小并不确定,因此我们需要使用一个JScrollPane来作为图片显示区域。
3.3.1初始化界面(init()方法)
首先,设置JFrame窗口的标题,接下来初始化画图区域,初始化为白色,然后再获取PENCIL_TOOL(铅笔)类型的Tool,创建各种鼠标监听器,并在监听的执行方法中调用Tool的相应方法,最后获取左边工具栏面板、下面菜单栏面板、菜单,并把这些面板与画图获取加到JFrame中,见以下代码。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
publicvoidinit(){
//设置标题
this.setTitle("看图程序");
//设置大小
this.setPreferredSize(newDimension(width,height));
//创建菜单
createMenuBar();
//创建工具栏
JPaneltoolBar=createToolPanel();
//把工具栏和读图区加到JFrame里面
this.add(toolBar,BorderLayout.NORTH);
this.add(newJScrollPane(label),BorderLayout.CENTER);
//设置为可见
this.setVisible(true);
this.pack();
}
首先是为JFrame设置标题,接下来设置大小,然后调用本类的createMenuBar()方法去创建菜单栏、调用createToolPanel()方法去创建工具栏,最后把菜单栏和图片显示区加到JFrame中(图片显示区只是一个JLabel)。
以上代码中的黑体部分,使用一个createToolPanel的方法来创建菜单,该方法将在下面章节中实现。
3.3.2创建菜单栏
菜单栏,必须有事件响应,所以,先为菜单定义一个事件监听器,见以下代码。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
//加给菜单的事件监听器
ActionListenermenuListener=newActionListener(){
publicvoidactionPerformed(ActionEvente){
service.menuDo(ImageFrame.this,e.getActionCommand());
}
};
这个事件监听器实现了ActionListener中的actionPerformed方法,是响应用户操作的方法,方法里面的service类就是我们的业务逻辑处理类ImageService的一个单态实例。
有了这个事件监听器,就可以一次性创建出所有的菜单(用数组定义好菜单文字等东西的形式),见以下方法。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
publicvoidcreateMenuBar(){
//创建一个JMenuBar放置菜单
JMenuBarmenuBar=newJMenuBar();
//菜单文字数组,以下面的menuItemArr一一对应
String[]menuArr={"文件(F)","工具(T)","帮助(H)"};
//菜单项文字数组
String[][]menuItemArr={
{"打开(O)","-","退出(X)"},
{"放大(M)","缩小(O)","-","上一个(X)","下一个(P)"},
{"帮助主题","关于"}
};
//遍历menuArr与menuItemArr去创建菜单
for(inti=0;i//新建一个JMenu菜单
JMenumenu=newJMenu(menuArr[i]);
for(intj=0;j//如果menuItemArr[i][j]等于"-"
if(menuItemArr[i][j].equals("-")){
//设置菜单分隔
menu.addSeparator();
}else{
//新建一个JMenuItem菜单项
JMenuItemmenuItem=newJMenuItem(menuItemArr[i][j]);
menuItem.addActionListener(menuListener);
//把菜单项加到JMenu菜单里面
menu.add(menuItem);
}
}
//把菜单加到JMenuBar上
menuBar.add(menu);
}
//设置JMenubar
this.setJMenuBar(menuBar);
}
图片浏览器的菜单是这样的结构:
文件(F)
打开(O)
退出(X)
工具(T)
放大(M)
缩小(O)
上一个(X)
下一个(P)
帮助(H)
帮助主题
关于
从代码中可以看到,程序用两个数组把这两层菜单的文字保存了进去,两个数组一起遍历,每次都创建一个菜单项(JMenuItem),并为这个菜单项增加上前面定义的事件监听器,然后把这个菜单项加到JMenu中。
每次遍历完 第一个数组,都把这个JMenu加到JMenuBar中。
遍历完所有数组,就把这个JmenuBar加到JFrame里面,创建菜单的过程就完成了。
3.3.3创建工具栏
这里的工具按钮,为了美观,想用图片的方式创建JButton,这里就要用到AbstractAction,也就是我们扩展的ViewerAction类,首先是用ViewerAction的ViewrAction(ImageIconicon,Stringname,ViewerFrameframe)去创建一个ViewrAction,参数里面的icon对象就是从本地路径中读了图标的图标类,然后以这个ViewerAction对象为参数去创建一个JButton。
见以下代码。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
publicJPanelcreateToolPanel(){
//创建一个JPanel
JPanelpanel=newJPanel();
//创建一个标题为"工具"的工具栏
JToolBartoolBar=newJToolBar("工具");
//设置为不可拖动
toolBar.setFloatable(false);
//设置布局方式
panel.setLayout(newFlowLayout(FlowLayout.LEFT));
//工具数组
String[]toolarr={"open","last","next","big","small"};
for(inti=0;iViewerActionaction=newViewerAction(
newImageIcon("img/"+toolarr[i]+".gif")
toolarr[i],this);
//以图标创建一个新的button
JButtonbutton=newJButton(action);
//把button加到工具栏中
toolBar.add(button);
}
panel.add(toolBar);
//返回
returnpanel;
}
以上代码的黑体部分,我们使用了JButton来创建工具栏的图标,每一个JButton对象都使用ViewerAction作为构造参数,但是需要注意的是,各个JButton之间并不是共享一个ViewerAction的实例。
创建完菜单与工具栏后,可以运行查看具体的效果,主界面的效果如图3.4所示。
图3.4图片浏览器主界面
在本例中,图片浏览器的功能相对较为简单,因此界面也是较为简洁。
如果想做更强大的图片浏览器,可以参考ACESee或者Windows图片浏览器等功能。
3.4实现图片浏览的操作
ViewerService类主要是处理图片浏览器的大部分业务逻辑,包括打开图片、关闭浏览器、放大图片、缩小图片、浏览上一张图片、浏览下一张图片等功能,在这里需要再做一次说明,ViewerService是有状态的Java对象。
3.4.1实现工具栏点击
我们在3.2.5中创建了一个ViewerAction的类,主要用于处理工具栏的点击事件,当用户点击了工具栏的某个操作时,就会执行ViewerAction的actionPerformed的方法。
我们在3.3.3中创建工具栏时,使用了以下代码。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
String[]toolarr={"open","last","next","big","small"};
for(inti=0;iViewerActionaction=newViewerAction(
newImageIcon("img/"+toolarr[i]+".gif")
toolarr[i],this);
//以图标创建一个新的button
JButtonbutton=newJButton(action);
//把button加到工具栏中
toolBar.add(button);
}
以上代码中使用了“open”、“last”等字符串用来标识应该使用ViewerService的哪个方法,那么就意味着我们需要在actionPerformed方法中作出这些判断:
if(this.name.equals("open")){
//打开文件对话框
}elseif(this.name.equals("last")){
//上一下图片
}
…
本章中只有5个Action,就需要写5次的if…else,对于这样的代码,我们在本书的第二章(仿Windows计算器)中已经出现,当前并没有提供任何的解决方案,但是如果程序中出现如些之多的if…else,那么我们就需要想办法去解决。
接下来,创建一个Action的接口,提供一个execute的方法。
代码清单:
code\viewer\src\org\crazyit\viewer\action\Action.java
publicinterfaceAction{
/**
*具体执行的方法
*@paramservice图片浏览器的业务处理类
*@paramframe主界面对象
*/
voidexecute(ViewerServiceservice,ViewerFrameframe);
}
编写了接口Action后,我们定义了一个execute的方法,那么,我们可以为该Action新建实现类,例如有一个打开文件对话框的Action,那么我们就新建一个OpenAction,该类实现Action接口。
以下代码是OpenAction的具体的实现。
代码清单:
code\viewer\src\org\crazyit\viewer\action\OpenAction.java
publicvoidexecute(ViewerServiceservice,ViewerFrameframe){
//打开文件对话框
}
提供了这个OpenAction后,我们需要修改创建工具栏的代码,换一种方式创建工具栏。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerFrame.java
//工具数组
String[]toolarr={"org.crazyit.viewer.action.OpenAction",
"org.crazyit.viewer.action.LastAction",
"org.crazyit.viewer.action.NextAction",
"org.crazyit.viewer.action.BigAction",
"org.crazyit.viewer.action.SmallAction"};
for(inti=0;iViewerActionaction=newViewerAction(newImageIcon("img/"
+toolarr[i]+".gif"),toolarr[i],this);
//以图标创建一个新的button
JButtonbutton=newJButton(action);
//把button加到工具栏中
toolBar.add(button);
}
将原来的字符串更换为某个Action实现类的全限定类名,那么在构造ViewerAction的时候,就可以使用这个参数去创建具体的某个实现类。
为ViewerAction编写一个工具方法,使用反射得到Action接口的某个实现类。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerAction.java
privateActiongetAction(StringactionName){
try{
if(this.action==null){
//创建Action实例
Actionaction=(Action)Class.forName(actionName).newInstance();
this.action=action;
}
returnthis.action;
}catch(Exceptione){
returnnull;
}
}
以上的黑体代码,使用了反射来创建一个实例,并且该实例在ViewerAction中只有一个实例,由于该方法在ViewerAction中,所以我们在构造ViewerAction的时候,将对应的处理类传入即可。
得到具体的某个Action实现类后,在实现ViewerAction的时候,我们就可以不必使用那堆烦人的if…else了,直接通过以上的工具方法(getAction)得到相关的Action实现类,再调用Action的execute方法即可。
代码清单:
code\viewer\src\org\crazyit\viewer\ViewerAction.java
publicvoidactionPerformed(ActionEvente){
ViewerServiceservice=ViewerService.getInstance();
Actionaction=getAction(this.actionName);
//调用Actio