接著上一部分,繼續分析開頭提出的幾個問題。
?
業務處理類(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方法。這一步用到了命令模式:
在創建工具欄上的按鈕時,將原來的字符串更換成實現類的全限定名:
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來找到上一張文件或下一張文件。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
