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

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

 

业务处理类(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++){
			
			//为了美观,想用图片的方式创建JButton
			ViewerAction 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的所有FileFilter
				FileFilter [] 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来找到上一张文件或下一张文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值