轉載:http://blog.csdn.net/chenzhiya/article/details/2274181
Java實用經驗Swing總結
前言
本文前言部分為我的一些感想,如果你只對本文介紹的Java實用技巧感興趣,可以跳過前言直接看本文的內容。
本文的寫作動機來源於最近接給人家幫忙寫的一個小程式,主要用於管理分期付款的貨款的一系列管理,包括到期款的紀錄,到期款利息的計算,為提前付款的使用者提供一些返款獎勵等等,這些與本文無關自不必細說。為了儘快完成任務,我自然選擇了我用得最多的Java來實現。經過2周的勞動,順利完成了任務,明天就可以去交差,但是這一刻我卻忽然有些其他的想法。誠然這樣的活原本屬於體力勞動,類似的活我也做過不止一次,對於很多高人來說,沒什麼值得一提的,以前我也只是交差收錢了事,但這一次我卻多了一些想法,使我不吐不快。
在程式的實現過程中,我遇到了個小問題,就是計算兩個日期的差。由於以前常用的Date類的大多數方法都被標記為“deprecate”,所以我決定用Calender作為計算日期的主力。但是大多數參考書上都是由關於Calender的日期格式,Locale的設定,常量的含義等方面的講解,卻怎麼也找不到這樣一個簡單卻常用的任務怎麼實現(註:這也不能怪我懶惰,作為這樣一個程式來說,如果有正確且成熟的方法,誰還會去花大量時間仔細研究API呢。反正這個類可能在今後的幾個月甚至幾年都用不上,現在記住到時候也都忘了L)。於是在我google了好一陣之後,終於在某人的Blog上找到了用Calender計算日期差的方法。在那一刻我真有久旱逢甘雨之感。博主可能是一時興起,也有可能是興趣所在,但無論是什麼原因,他的工作都為我提供了很大的方便。有了他的程式碼範例,我可以不再去逐個尋找Java-Doc裡面的API,然後挑出幾個來嘗試解決問題,最後再寫個demo驗證這一繁複的過程了。
再回想一下我完成這個程式的過程,由於以前做過一些類似的程式,我可以將裡面的很多部分以直接應用到這個程式中,節省了大量的時間,讓我可以更專註於核心業務的實現當中。然而或許是出於懶惰,或許是沒有時間,又或許原來的是Blog沒有多少人關注,我都沒有將這些大多數人都可能會用得上的東西放到網上。
再聯想一下國外開源工作者對中國程式員的評價—“只擷取,不貢獻”,就覺得人家說得十分對。自己就用著免費的J2SDK語言,免費的Eclipse,免費的JFreeChart,免費的JasperReport……,卻從來沒能夠給人家貢獻哪怕一行代碼。這樣也就算了,但是類似於一些力所能及的東西,例如可能每個Java程式員都會碰到的一些小問題,小技巧,常常出現的錯誤,為什麼我就不能把他們貼出來供人分享呢。說不定就會幫到某位哥們解決大問題,更有可能你的幾句話就能節省別人幾分鐘甚至幾小時的時間。如果每個人都能在業餘時間把自己的一些心得體會貼出來,相信更多的人將因此受益。當你遇到問題的時候,才能心安理得的去Google或Baidu。相信這也是技術論壇和技術Blog的初衷吧,畢竟這個世界並不是只有錢才是最重要的原動力。
1 改變Swing應用程式的預設字型/字型大小
經常使用Swing作為程式UI的人可能會注意到,Swing組件預設顯示文字的字型大小為11。這對於英文顯示毫無問題,但是如果用這個字型大小顯示中文的話,這麼小的字型大小就會使程式變得很難看。我當年在用IReport0.56的時候就發現他的功能表列和彈出的Dialog裡的字很難看,但是將字型大小調大之後就好多了。雖然在最近版本的JDK裡似乎修正了這個字型問題,但是如果你的程式必須使用以前版本的JDK的話,這個問題就需要處理一下,下面就是一個不錯的解決方案:
Font vFont = new Font("Dialog", Font.PLAIN, 13);
UIManager.put("ToolTip.font", vFont);
UIManager.put("Table.font", vFont);
UIManager.put("TableHeader.font", vFont);
UIManager.put("TextField.font", vFont);
UIManager.put("ComboBox.font", vFont);
UIManager.put("TextField.font", vFont);
UIManager.put("PasswordField.font", vFont);
UIManager.put("TextArea.font", vFont);
UIManager.put("TextPane.font", vFont);
UIManager.put("EditorPane.font", vFont);
UIManager.put("FormattedTextField.font", vFont);
UIManager.put("Button.font", vFont);
UIManager.put("CheckBox.font", vFont);
UIManager.put("RadioButton.font", vFont);
UIManager.put("ToggleButton.font", vFont);
UIManager.put("ProgressBar.font", vFont);
UIManager.put("DesktopIcon.font", vFont);
UIManager.put("TitledBorder.font", vFont);
UIManager.put("Label.font", vFont);
UIManager.put("List.font", vFont);
UIManager.put("TabbedPane.font", vFont);
UIManager.put("MenuBar.font", vFont);
UIManager.put("Menu.font", vFont);
UIManager.put("MenuItem.font", vFont);
UIManager.put("PopupMenu.font", vFont);
UIManager.put("CheckBoxMenuItem.font", vFont);
UIManager.put("RadioButtonMenuItem.font", vFont);
UIManager.put("Spinner.font", vFont);
UIManager.put("Tree.font", vFont);
UIManager.put("ToolBar.font", vFont);
UIManager.put("OptionPane.messageFont", vFont);
UIManager.put("OptionPane.buttonFont", vFont);
這段代碼用在程式的開始部分,可以有效地將Swing組件的顯示字型設定為我們在vFont所設定的內容。 1.1 讓視窗更好地置中顯示
無論是頂層組件JFrame還是對話方塊JDialog,讓他們的視窗置中顯示是一個很常見的問題,因為他們預設總是從左上方彈出來,這也太不爽了。對於這個問題,JBuilder應用程式產生嚮導給出瞭解決方案:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height)
frameSize.height = screenSize.height;
if (frameSize.width > screenSize.width)
frameSize.width = screenSize.width;
frame.setLocation((screenSize.width-frameSize.width)/2,screenSize.height-frameSize.height) / 2);
這個方法對於大多數視窗組件來說都足夠了,但是還有其他問題存在,比如說解析度和顯示器的尺寸都會導致應用程式視窗“變形”,明明在17寸顯示器1024*768解析度下顯示好好的視窗到了19寸的1280*800的寬屏下就會被“拉”得很“長”。於是,雖然有布局管理器幫我們管理展開後組件的放置,但仍然解決不了拉長後帶來的美觀問題。我的經驗是,對於某些視窗,由於它被“拉長”之後由於其內部組件之間的間隙變大,會顯得很難看。所以應該為他們設定一個最合適的顯示大小。在置中顯示的時候只調整位置而不改變大小,這樣就不會影響視窗的美觀。所以我們只需要對上面的代碼小改一下即可,以JFrame為例:
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame.setPreferredSize(new Dimension(512,450));
int frameWidth = this.getPreferredSize().width;
int frameHeight = this.getPreferredSize().height;
frame.setSize(frameWidth, frameHeight);
frame.setLocation((screenSize.width - frameWidth) / 2,(screenSize.height - frameHeight) / 2); 2 自訂JFrame的關閉事件
有的時候,當使用者關閉應用程式視窗的時候,我們可能希望程式在結束之前儲存一些必要的資料。對於這種需求,我們有兩種備選方案: 2.1 擷取程式關閉的“鉤子”
Runtime.getRuntime().addShutdownHook(shutdownHook);
protected Thread shutdownHook = new PlatformShutdownHook();
protected class PlatformShutdownHook extends Thread {
public void run()
{
//一些清理工作在這裡進行……
}
}
通過這種方法,我們就可以在程式結束時獲得通知,以便進行一些儲存或清理的工作。然而這種方法的缺點是,在程式收到結束通知的時候,所有的UI組件已經被銷毀了,使用者此時看到的是程式已經結束。而事實上如果程式儲存需要花很長的時間的話,使用者是不能擷取任何資訊的,這是一個很糟糕的使用者體驗。因為如果這時使用者關機的話,程式就有可能丟失尚未儲存的資訊,而對於這一切,使用者並不知情。 2.2 處理JFrame關閉事件
為了在UI被銷毀之前收到程式結束的訊息,我們需要自行處理視窗關閉的事件。注意在這裡我們沒有採用addActionListener(……)方法,因為這樣做只能讓我們在視窗關閉之後收到通知,這樣就與上面的方法沒什麼區別了。
我們需要在JFrame的建構函式中設定:
//設定標誌,讓MainFrame自己接收視窗事件
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
然後再實現下面的函數:
protectedvoid processWindowEvent(final WindowEvent pEvent) {
if (pEvent.getID() == WindowEvent.WINDOW_CLOSING) {
/**防止使用者多次點擊“關閉”按鈕造成重複儲存**/
if( !isClosing ) isClosing = true;
else return;
//處理JFrame關閉事件……
}else{
//忽略其他事件,交給JFrame處理
super.processWindowEvent(pEvent);
}
}
如此一來,我們就可以在視窗被關閉之前通知使用者程式正在儲存資料的資訊,例如後面提到的InfiniteProgressPanel可以顯示的內容。 3 日期選擇組件與JDialog的衝突問題
由於很多應用程式都需要使用者輸入日期,卻又怕使用者輸入的日期格式錯誤,所以日期選擇組件便應運而生。雖然我們很需要它,但是網上絕大多數的組件都是需要給錢的。在找到SwingX之前,我找到的唯一能夠免費使用的行事曆群組件就是一個名為DateChooser的JDialog:
看樣子很不錯,它支援中文,對於今天高亮顯示,可以調整年分和月份……一切都非常符合要求。但是這麼好的組件卻不能用在我的程式裡,原因是在我的程式中,調用這個組件的組件也是一個JDialog,並且設定了setAlwaysOnTop(true)—即總在最最上層顯示。由於DateChooser也設定了在最最上層顯示,這就導致了它和其父組件的顯示衝突,最終結果是DateChooser不能正常顯示。對於這個問題,我最終使用SwingX的組件DatePicker來代替DateChooser完成選擇日期的使命,慣於DatePicker的使用我將來會在“SwingX使用詳解”中提到,這裡就不再細說。但是這個問題仍然值得我們注意,即如果一個視窗組件是設定了總在最最上層顯示的JDialog,那麼就不要以這個JDialog為父組件來彈出其他JDialog,以避免衝突的發生。
4 JTable的實用技巧
無論對於什麼樣的一個應用程式來說,用表格的形式來顯示資料是再平常不過的事情了。於是JTable就成為我們在所有Swing組件中最不可或缺的朋友。對於JTable的操作,大多數情況下我們都可以不假外求,因為JDK內建的例子SwingSet2給我們展示了足夠多的功能。
在這個例子裡,我們可以改變儲存格的間距,行高,選擇類型(Selection Style),是否顯示水平線,甚至可以將表格內容列印出來。其中,表格除了文字之外還可以包含其他組件和內容,如SwingSet2種就加入了可以選擇顏色的JComboBox和喜愛的食物所代表的圖片。
但有些時候,我們還會有一些其他的需求。例如說為了保護我們的眼睛,我們希望表格的內容是帶有間隔色的,如奇數行顯示藍色,而偶數行顯示白色。又或者我們希望表格中某些列的內容是可編輯的,而且他列的內容是不可編輯的。又或者讓表格中的列帶有排序的功能,能讓我們點一下表頭它就自己按照從低到高或從高到低的順序自行排列。最後我們希望表格的表頭和儲存格力的內容能夠置中顯示。讓我們一個一個來實現這些功能。 4.1 間隔色表格及儲存格/表頭置中顯示
JTable的API並沒有為我們提供更改表格行或列的顏色的能力。但是我們知道,表格的表頭和內容的呈現形式都是由相應的Renderer來控制的,所以我們只需要繼承儲存格預設的Renderer並作相應的修改就可以達到目的:
由於實現了介面