读书人

图片浏览器-学习札记(二)

发布时间: 2012-12-22 12:05:06 作者: rapoo

图片浏览器--学习笔记(二)

接着上一部分,继续分析开头提出的几个问题。

?

业务处理类(ViewerService)主要处理图片浏览器的大部分业务逻辑,包括打开图片、放大图片、缩小图片、浏览上一张图片、浏览下一张图片。


在这个类中使用了单态模式,即类内存中只能创建一个实例对象,将类的构造方法的访问权限定义为private(于是无法在类的外部产生类的对象),在类的内部定义一个可以返回类的对象的方法,这个方法需要是静态方法,(于是可以在类的外部调用该静态方法返回类的对象),同时静态方法只能访问静态变量,所以类内部产生的对象的变量也要是静态的,代码如下:

//私有构造器private ViewerService(){}private static ViewerService service = new ViewerService();//获取单态实例public static ViewerService getInstance() {return service;}

这样做的原因是该业务处理类的对象不是无状态的JAVA对象,该类中定义了一些保存业务状态的属性,包括当前浏览的文件目录、当前浏览的图片文件、文件目录的文件集合、图

片放大和缩小的比例。如果访问的是同一个实例,这些表示业务状态的属性就会被所有访问者共享,其中一个访问者改变了属性的值,其他访问者也会受到影响。
(对于这一部分,我也不是特别明白,尽量去理解吧,总会有豁然开朗的一天)

?

不管是菜单栏的菜单项还是工具栏的按钮,都必须要有事件响应,这里有两种实现方案。第一种,是初学者比较容易想到的,直接为菜单定义一个事件监听器,代码如下:

//为菜单定义事件监听器ActionListener menuListener= new ActionListener(){public void actionPerformed(ActionEvent e){service.menuDo(ViewerFrame.this,e.getActionCommand());}};//新建一个菜单项,并注册事件监听器JMenuItem menuItem = new JMenuItem(MenuItemArr[i][j]);menuItem.addActionListener(getActionListener());这样的方法会带来一系列的if…else,请看代码://实现菜单的单击public void menuDo(ViewerFrame frame, String cmd) {//打开if(cmd.equals("打开(O)")){open(frame);}//放大if(cmd.equals("放大(M)")){zoom(frame,true);}//缩小if(cmd.equals("缩小(O)")){zoom(frame,false);}//上一个if(cmd.equals("上一个(X)")){last(frame);}//下一个if(cmd.equals("下一个(P)")){next(frame);}//退出if(cmd.equals("退出(X)")){System.exit(0);}}

下面看第二种实现方式,以工具栏上的按钮的事件响应为例。
在看第二种实现方式之前,先用第一种方式来实现:
与菜单实现不同的是,这里创建一个ViewerAction类,在这个类中定义事件监听器。

String[] toolarr = {"open","last","next","big","small"};for(int i = 0;i<toolarr.length;i++){ViewerAction action = new ViewerAction(new ImageIcon("img/"+ toolarr[i]+ ".gif", toolarr[i],this));        //以图标创建一个新的button        JButton button = new JButton( action );        //把button加入到工具条中        toolbar.add(button);}

使用了"open","last","next","big","small"等字符串来标识应该使用ViewerService的哪些方法,这意味着我们需要在actionPerformed方法中来做这些判断。

public class ViewerAction extends AbstractAction {String name =null;ViewerFrame frame = null;public ViewerAction(ImageIcon icon, String name, ViewerFrame frame) {super(name,icon);this.name = name; this.frame = frame;}public void actionPerformed(ActionEvent e) {if(this.name.equals("open")){//在这里调用打开文件对话框的方法}else if(this.name.equals("last")){//在这里调用上一张图片的方法} else if(this.name.equals("next")){//在这里调用下一张图片的方法} else if(this.name.equals("big")){//在这里调用放大图片的方法} else if(this.name.equals("small")){//在这里调用缩小图片的方法}}}

下面使用另一种方法,可以避免这些if… else语句。
先是创建一个Action接口,提供一个execute方法。这一步用到了命令模式:


图片浏览器-学习札记(二)
接下来为Action接口新建实现类,


图片浏览器-学习札记(二)
在创建工具栏上的按钮时,将原来的字符串更换成实现类的全限定名:

String [] toolarr = {"org.crazyit.viewer.OpenAction","org.crazyit.viewer.LastAction","org.crazyit.viewer.NextAction","org.crazyit.viewer.BigAction","org.crazyit.viewer.SmallAction"};String [] toolimg = {"open","last","next","big","small"};for(int i=0;i<toolarr.length;i++){//为了美观,想用图片的方式创建JButtonViewerAction action = new ViewerAction(new ImageIcon("images/"+toolimg[i]+".png"),toolarr[i],this);//为工具栏创建按钮JButton button = new JButton(action); button.setHideActionText(true);//默认情况下对图片的操作按钮不可用if(i!=0){button.setEnabled(false);}//将按钮添加到工具栏上toolBar.add(button);}

那么在构造ViewerAction用这个参数来记录下具体的某个实现类的类名,为ViewerAction编写一个工具方法,使用反射得到某个实现类的一个实例:

private Action getAction(String actionName) {try{if(this.action ==null){//创建Action实例Action action = (Action) Class.forName(actionName).newInstance();this.action = action;}return this.action;}catch(Exception ex){return null;}

注意,使用反射来创建的实例是唯一的,在事件监听器中使用该工具方法,返回该实例,并调用该实例的execute()方法。
用这种方法就避免了大量的if…else,也具有一定的扩展性,使用了多态。

?

如何实现打开图片?
如何放大或者缩小图片?
如何实现浏览“上一张”或“下一张”图片?

首先用ViewerFileChooser对象的showOpenDialog方法弹出一个文件选择框,在用户选择了某个图像文件后,点击确定按钮后,该方法会返回一个值APPROVE_OPTION。

这时需要把这个文件所在的文件夹中的所有图像文件都缓存起来,这样是为了不用每次都去搜索这个文件夹内的文件,也是为了方便“上一张”和“下一张”的定位,缓存的文件都保存在currentFiles中,它的类型是ArrayList<File>。这里还要考虑问题的是,在用户第一次打开某个图片文件后,它所在的文件夹中的文件被缓存到了currentFiles中,在用户第二次打开某个图片文件后,它所在的文件夹可能已经发生了变化,这时需要把新的文件夹中的所有文件缓存到currentFiles中,所以在每次用户打开文件图片时,都要判断文件夹是否改变,如果改变了,要重新进行缓存:

if(cd != this.currentDirectory || this.currentDirectory==null){//获取fileChooser的所有FileFilterFileFilter [] fileFilters = fileChooser.getChoosableFileFilters();//获取读取到的文件File files[] = cd.listFiles();this.currentFiles = new ArrayList<File>();//将符合自定义过滤器的文件缓存下来for(File file : files){for(FileFilter filter: fileFilters){//如果是图片文件if(filter.accept(file)){//把符合过滤器的文件加到currentFiles中this.currentFiles.add(file);}}}}

如果文件路径发生了变化,或者这是第一次打开文件,就执行if语句中的代码。
对于图片的放大或者缩小,使用了Image中的一个叫getScaledInstance的方法。

ImageIcon newIcon = new ImageIcon(icon.getImage().getScaledInstance(width, -1, Image.SCALE_DEFAULT));

其中参数width表示将图像缩放到的宽度,height表示将图像缩放到的高度,hints表示指示用于图像重新取样的算法类型的标志,其实就是选择一种缩放算法。
如果width或height为负数,则替换该值以维持初始图像尺寸的高宽比。
对于取上一张或下一张图片的方法就相对容易了,因为在打开文件的时候,已经把文件夹下的文件缓存在了currentFiles中,只需要获得当前文件的索引,然后按照索引加1或索引减1来找到上一张文件或下一张文件。

读书人网 >编程

热点推荐