接著上一部分,繼續分析開頭提出的幾個問題。
?
業務處理類(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元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

