前两天,看了一本书,上面用java写了一个图片浏览器,突然就觉得比较感兴趣,上面介绍了思路,和大致的功能如何做,做一个图片浏览器,大致的界面是这样的
我们先来看一下这个图片浏览器有什么功能,1.打开图片;2.上一张下一张;3.放大缩小图片
就这么简单的三个功能,我们需要的东西却有很多,首先,外体的frame,然后是JMenuBar,还有用于放工具按钮的JToolBar,最后一个JLable用于放图片的。
我们要实现打开文件功能,那就需要文件选择器和文件过滤器,过滤掉那些不支持的文件格式,这个两个功能我们需要知道两个类:1.JFileChooser类;2.FileFilter类。用这两个类里面的某些功能来实现我们打开图片的操作。然后就是放大和缩小,这个就需要了解Image类里面的getScaledInstance方法。至于上一张下一张就要知道我们所写的这个图片浏览器是可以储存用户最近打开文件的路径的,对于路径最后一级的所有我们支持打开的文件,我们可以存在一个File类型的队列里,然后通过引索来得到上下图片的路径,然后打开它。这就是写简单的功能介绍。
下面我们可以分别建几个类,来实现这些功能,主界面类继承JFrame里面可以把工具栏,菜单栏,和放图片的区域加到上面,这里面工具栏按钮的实例化时,我们用Action来初始化它们,这个Action类可以用API查到,但查到的是这个类是一个接口,我们用一个类来实现这个接口,然后通过重写里面的actionPerformed方法,从而得到监听按钮的目的,我觉得这个方法更简便,可以不用再重新给按钮加监听器,并且直接初始化,而且到后面我们会发现,这有利于后面实现其他的各种功能,如何实现点击按钮后面的功能呢?这里我们可以再用一个类,里面写好各个按钮的功能,要想把按钮个各个功能一一对应,我们可以在监听器里面用很多if....else结构来实现,但是这样难免有些繁琐,而且在传参数时容易出错,我们可以再Action的实现类里面利用反射机制,可以再每次点击后得到点击后类的实例,从而来实现功能,所以我们要把每个按钮对应一个类,这个类里面用一个方法来实现功能,我们所调用的这些功能的方法都需要用到实现功能类和主界面类,所以可以用一个接口,里面定义一个实现功能的方法,然后每个功能用一个实现类,这样就能充分利用反射机制来实现共能了,这里可能我表述的不清楚,没关系,下面看代码就好了。
首先来把界面写好,把工具栏,菜单栏,图片显示区域加上。
import java.awt.BorderLayout; import java.awt.Dimension; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; public class ViewerFrame extends JFrame{ //实例化ViewerFrame类 private static ViewerFrame frame = new ViewerFrame(); //实例化一个JLabel对象用于插图片 JLabel imageLabel = new JLabel(); public static void main(String[] args) { frame.FrameUI(); } public void FrameUI(){ //设置大小 this.setSize(new Dimension(600,700)); //设置居中 this.setLocationRelativeTo(null); //设置标题 this.setTitle("PictureViewer"); //调用创建菜单栏方法 creatMenuBar(); //创建工具栏,并加到Frame北边 JPanel jpanel = creatToolPane(); this.add(jpanel,BorderLayout.NORTH); //不要设置布局为流布局,否则图片不容易显示 //this.setLayout(new BorderLayout()); //设置关闭方式 this.setDefaultCloseOperation(3); //添加滚动条 this.add(new JScrollPane(imageLabel),BorderLayout.CENTER); //设置可见 this.setVisible(true); //是窗口更加适应图片,自动 调整大小,这里不建议,可以尝试看看 //this.pack(); } //创建菜单栏方法 public void creatMenuBar(){ JMenuBar menubar = new JMenuBar(); //菜单栏文字选项 String[] menubarArray = {"文件(F)","工具(T)","帮助(H)"}; //二级菜单项文字数组 String[][] menu = {{"打开(O)","-","退出(X)"},{"放大(M)", "缩小(O)","-","上一个(X)","下一个(P)"},{"帮助主题","关于"}}; for(int i=0;i<menubarArray.length;++i){ //实例化菜单项 JMenu jmenu = new JMenu(menubarArray[i]); //添加菜单项 for(int j=0;j<menu[i].length;++j){ if(menu[i][j].equals("-")){ jmenu.addSeparator(); } else{ //实例化JMenuItem对象 JMenuItem jmi = new JMenuItem(menu[i][j]); //添加到JMenu上 jmenu.add(jmi); } } //把menu加到menubar上 menubar.add(jmenu); } //这里要用setJMenuBar方法,而不是add方法 this.setJMenuBar(menubar);; } //得到imageLabel方法 public JLabel getLabel(){ return imageLabel; } //创建工具栏方法 public JPanel creatToolPane(){ //实例化一个JPanel对象,承载ToolPane JPanel toolpane = new JPanel(); //实例化工具栏 JToolBar toolbar = new JToolBar(); //创建路径String数组来添加按钮 String[] buttonArray = {"PictureViewer.openAction","PictureViewer.lastAction","PictureViewer.nextAction", "PictureViewer.lastAction","PictureViewer.bigAction","PictureViewer.smallAction"}; //遍历循环实例化并加入按钮 for(String name : buttonArray){ //实例化ViewerAction对象,用于初始化button ViewerAction action = new ViewerAction(new ImageIcon("images/"+name+".jpg"),name,this); //用Action来初始化Button JButton button = new JButton(action); //button加到toolbar上 toolbar.add(button); } //把toolbar加到panel上 toolpane.add(toolbar); return toolpane; } }
这是主界面类的方法,这里面唯一不知道的东西是ViewerAction类,这个类就是用于我们所说的实例化button用到的,这里面重写actionPerformed方法,下面是这个类的代码
import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; public class ViewerAction extends AbstractAction{ //声明一个ViewerFrame对象 private ViewerFrame frame = null; //用于存储名字的String String ActionName = ""; //声明一个Action对象 Action action = null; //构造方法 public ViewerAction(ImageIcon image,String ActionName,ViewerFrame frame){ //实现父类的构造方法,不要忘记 super("",image); this.ActionName = ActionName; this.frame = frame; } //重写actionPerformed方法 @Override public void actionPerformed(ActionEvent e) { ViewerService service = ViewerService.getInstance(); Action vieweraction = getAction(ActionName); vieweraction.dothis(frame,service); } //得到Action的方法,这里用到了反射 public Action getAction(String ActionName){ try{ if(this.action == null){ Action action = (Action)Class.forName(ActionName).newInstance(); this.action = action; } return this.action; } catch(Exception E){ return null; } } }
这里就是Action类,它可以监听工具栏按钮,并且初始化按钮,getAction方法用到了反射,因为后面要用几个Action类,运行程序运行的时候得到实例用于实现功能。下面来看一下那几个Action的代码,这几个Action都是实现类一个借口
public interface Action { //用于实现功能方法的声明 public void dothis(ViewerService service,ViewerFrame frame); }
public class openAction implements Action{ @Override public void dothis(ViewerService service, ViewerFrame frame) { service.open(frame); } }
public class smallAction implements Action{ @Override public void dothis(ViewerService service, ViewerFrame frame) { service.BigOrSmall(frame,false); } }
public class bigAction implements Action{ @Override public void dothis(ViewerService service, ViewerFrame frame) { service.BigOrSmall(frame,true); } }
public class lastAction implements Action{ @Override public void dothis(ViewerService service, ViewerFrame frame) { service.LastOrNext(frame,true); } }
public class nextAction implements Action{ @Override public void dothis(ViewerService service, ViewerFrame frame) { service.LastOrNext(frame,false); } }
这就是这几个类,由这几个类,就可以看得懂ViewerAction类中的代码了,最后,一个很重要的核心任务就是在 ViewerService类中实现各个功能,但在写这个类之前,我们还要想到,如果打开文件的话,我们需要一个文件过滤器,这个文件过滤器又是由一个文件选择器来承担,所以,要先做好文件这方面的工作才好进行,下一步。文件过滤器类是文件选择器的内部类。
package shane.PictureViewer; import java.io.File; import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; public class FileChooser extends JFileChooser{ //构造方法 public FileChooser(){ super(); setAcceptAllFileFilterUsed(false); //增加文件过滤器方法 addFilter(); } public FileChooser(String currentDirectoryPath){ super(currentDirectoryPath); setAcceptAllFileFilterUsed(false); addFilter(); } //添加文件方法 public void addFilter(){ //向用户可选择的文件过滤器列表添加一个过滤器 this.addChoosableFileFilter(new MyFileFilter(new String[] {".JPG","JPEG","JPE","JFIF"},"JPEG(*.JPG,*JPEG,*JPE,*JFIF")); this.addChoosableFileFilter(new MyFileFilter (new String[]{".BMP"},"BMP(*BMP)")); this.addChoosableFileFilter(new MyFileFilter(new String[] {".GIF"},"*.GIF")); this.addChoosableFileFilter(new MyFileFilter(new String[] {".BMP",".JPG",".JPEG",".JPE",".JFIF",".GIF"},"所有图形文件")); } /* * MyFileFilter继承自FileFilter,文件过滤器 * 内部类 */ class MyFileFilter extends FileFilter{ //存储后缀名String数组 String[] suffar; //描述 String discription; //构造方法 public MyFileFilter(String[] suffar,String discription) { super(); this.suffar = suffar; this.discription = discription; } //重写接受文件的方法 @Override public boolean accept(File f) { //遍历suffar数组,比较是否能接受文件 for(String isaccept : suffar){ //先把名字改成大写,然后比较是否是以isaccept结尾 if(f.getName().toUpperCase().endsWith(isaccept));{ return true; } } //如果是目录就返回true,反之则返回false return f.isDirectory(); } @Override public String getDescription() { return this.discription; } } }
打开文件方法
//打开图片方法 public void open(ViewerFrame frame){ if(filechooser.showOpenDialog(frame) == filechooser.APPROVE_OPTION){ //储存此文件路径 currentFile = filechooser.getSelectedFile(); //得到图片路径 String iconDrictory = filechooser.getSelectedFile().getPath(); //为了检验此文件夹是否与上次打开的文件夹一致,我们采取下面的方法 File isSameDirectory = filechooser.getCurrentDirectory(); if(isSameDirectory != currentDirectory || currentDirectory == null){ //用于接受所有的文件过滤器 FileFilter[] filefilter = filechooser.getChoosableFileFilters(); //用于得到该文件夹的所有文件路径 File[] file = isSameDirectory.listFiles(); //用于筛选出适合的文件,添加到this.file的队列中 for(File isfile : file){ for(FileFilter filefle : filefilter){ if(filefle.accept(isfile)){ this.file.add(isfile); } } } } //设置图片到frame的label中 ImageIcon icon = new ImageIcon(iconDrictory); frame.getLabel().setIcon(icon); } }
//放大缩小方法 public void BigOrSmall(ViewerFrame frame, boolean enLarge){ //获取放大或者缩小比例 double enLargeRange = enLarge? 1 + Biger: 1 - Biger; //System.out.println(ScrollAmount); //获取目前的图片 ImageIcon icon = (ImageIcon) frame.getLabel().getIcon(); if(icon != null){ int width = (int) (icon.getIconWidth() * enLargeRange); //获取改变大小后的图片 ImageIcon newIcon = new ImageIcon(icon.getImage(). getScaledInstance(width, -1, Image.SCALE_DEFAULT)); //显示改变的图片 frame.getLabel().setIcon(newIcon); } }
就这么多,剩余的还有上下图片方法,就不在这展示了,感觉好像大多数都是代码,上下图片方法下次有机会再上传。