今天在使用Java做系統托盤圖標(TrayIcon),需要為其增加一個右鍵彈出菜單(PopupMenu),在使用菜單項(MenuItem)時,遇到了一個非常痛苦的事情:中文亂碼~~~~。這個問題不經常碰到,但是一旦碰到就頭疼了,網上一搜,方法一大堆,但很少有方法能解決自己的問題,畢竟情況不一樣。
今天搞了一天,終於搞出了一套方案。
先說一下出問題的原因:
1. 本地系統區域語言字符集的問題,我們的系統可能默認的字符集為GB2312或GBK。
2. Java源文件編碼字符的問題,如果使用eclipse可以查看一下java source文件的編碼方式。
3. javac編譯時的字符問題,這一個問題很容易被忽略,運行一下javac可以看到有一個encoding的參數可以設置----這一個很重要。
4. 要讀取文件的編碼和讀取時的字符集設置問題,如果我們要用的字符串不是硬編碼在java源碼中,而是從資源文件中讀取時,就要注意這個問題了。
要解決問題需要做到如下:
1. 本地系統的語言字符可以不用管,主要看后面三項。
2. Java源文件的編碼,這一點很重要,最好使用utf8編碼。
我們在編輯文件時默認使用的是本地系統的字符集(如GBK),所以對Java源文件要進行字符轉換或提前做好設置,對於UE使用 文件--->轉換--->...到UTF-8(Unicode 編輯),對於eclipse設置 Window-->Preferences-->General-->Content Types--> Java Source File。具體操作此處不詳述。
3. javac編譯時的參數設置,增加encoding參數,如:javac -encoding utf8 Test.java
對於這一點要特別注意,eclipse的編譯器是沒有使用該參數的,我也沒找到該如何設置該參數(注意這里是編譯參數javac, 不是運行參數java或jvm, 不是run config中配置的),所以遇到這個問題的情況下不能使用eclipse來編譯了,至少在能配置javac之前是這樣。
不能使用eclipse了怎么辦呢,手工不是太可能,有幾種方案:
1. 手工編譯使用到MenuItem的類,目前只發現java.awt包會存在這個問題,swing包能很好的解決這種字符集變換的問題。
2. 使用其它編譯方式,如ant、Maven,它們都能配置javac。我現在就使用maven,只需要為它的編譯器加入encoding配置,如下:
Java代碼
org.apache.maven.plugins
maven-compiler-plugin
2.3.2
1.6
1.6
utf8
....
org.apache.maven.plugins
maven-compiler-plugin
2.3.2
1.6
1.6
utf8
....
4. 要讀取的文件的字符編碼和讀取流編碼,對一個資源文件,我們也要設置其編碼為utf8(參照第2條,跟設置java源文件編碼是一樣的),同時在使用流讀取時也要設置流的讀取編碼(這個網上說java字符流的默認的是jvm的編碼,跟系統字符集一致)。
選說說我的場景吧,我要從一個文件中讀取字符串做為MenuItem的Label值(打算做國際化,呵呵),所以就需要對這個資源文件做處理,讀取流代碼如下:
Java代碼
BufferedReader br =newBufferedReader(newInputStreamReader(newFileInputStream(langFile),"utf8"));
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(langFile), "utf8"));
這里還要注意的是:在復制這個資源文件時,最好也設置一下編碼,如用ant復制時:
Xml代碼
tofile="D:\klaudisk-client\target\conf\settings.ini"overwrite="true"encoding="utf8"/>
tofile="D:\klaudisk-client\target\conf\settings.ini" overwrite="true" encoding="utf8"/>
好了,做到以上條件問題應該解決了。
在實際的過程中我們可以通過以下步驟一步一步來達到以上條件,每步都可以測試。
1. 查看源文件的編碼
這一點很簡單,對於eclipse查看一下java source file編碼方式就行了,保險一點就是修改一下編碼方式,會發現原來的java文件(含中文的)亂碼了,再改回來,就好了。 編碼方式請設置為utf8
2. 編譯編碼的測試
可以先寫一個小例子,使用硬編碼的方式加入中文,如MenuItem m = new MenuItem("中文"),你用eclipse運行后可以發現這個menu顯示的是亂碼(全是小方框)。然后你用命令行的方式編譯一下:javac -encoding utf8 Test.java。然后命令行運行,如果發現顯示正常了,則這及以前的測試通過了,如果還是亂碼,則需要你再次確認第1步。
3. 在前2部都確定成功的情況下再考慮讀取資源文件的測試,這個參照前面說的就行了。
--OK,問題應該能解決了,這種方式還可以應用到其它中文亂碼的問題上。
補充說明的是:awt不是不支持中文,是編碼轉換不夠智能。在swing中就解決了這些問題。
關於這個TrayIcon,我看oracle那邊已經提交了一個bug,意思是說要編寫一個對應的swing的JTrayIcon,從而使用JPopupMenu和JMenuItem。但這個bug不是因字符問題提交的。
對於MenuItem亂碼的問題,還可以使用JPopupMenu和JMenuItem來替代,參照如下,但情況不理想(彈出時必須要選擇一個菜單,否則不消失):
Java代碼
ImageIcon icon =newImageIcon(UITest.class.getResource("16.gif"));
TrayIcon tray = newTrayIcon(icon.getImage());
tray.setImageAutoSize(true);
SystemTray.getSystemTray().add(tray);
tray.addMouseListener(newMouseAdapter() {
@Override
publicvoidmouseReleased(MouseEvent e) {
if(e.isPopupTrigger()) {
finalJPopupMenu pop =newJPopupMenu();
JMenuItem m1 = newJMenuItem("中文");
pop.add(m1);
pop.add(newJMenuItem("主題"));
pop.setLocation(e.getX(), e.getY());
pop.setInvoker(pop);
pop.setVisible(true);
}
}
});
ImageIcon icon = new ImageIcon(UITest.class.getResource("16.gif"));
TrayIcon tray = new TrayIcon(icon.getImage());
tray.setImageAutoSize(true);
SystemTray.getSystemTray().add(tray);
tray.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
final JPopupMenu pop = new JPopupMenu();
JMenuItem m1 = new JMenuItem("中文");
pop.add(m1);
pop.add(new JMenuItem("主題"));
pop.setLocation(e.getX(), e.getY());
pop.setInvoker(pop);
pop.setVisible(true);
}
}
});
另外,對於第4點,資源文件的問題,還可以使用Properties.load()方法來加載.properties文件(該文件是被java bin下的navie2asc程序處理過的文件),這樣就不用管第4點問題了,但問題很顯然,不方便不直觀。
---EOF---