Eclipse rap 開發經驗總結
1、 Eclipse rap專案目前的進度
目前rap 的最新版本是 rap 1.5M4,最穩定版本是 rap 1.4.1,我們專案目前使用的版本是rap 1.4.1,Rap目前已經支援的部分是 swt,JFace,rap目前暫時不支援的部分是 dwaw2d、gef關於對dwaw2d、gef的支援目前正在開發中,原計劃在eclipse E4版本中支援,目前改版本已經延遲了,而且暫時不支援dwaw2d、gef,至於什麼時候完成,就不得而知了.
關於版本和支援情況大家可以去這個頁面上看一下
http://www.eclipse.org/rap/downloads/
2、目前rap上存在的問題
1、執行速度
速度上面不同瀏覽器支援情況不同,使用chrome瀏覽器速度最快,firefox其次,IE瀏覽器(所有IE核心的都一樣)最慢,而且不支援IE6(IE6上面跑介面上顯示有些小問題),其他瀏覽器沒有試過.大家有時間可以驗證下,目前官方上釋出支援的瀏覽器有
Firefox 2+ 、IE 7+ 、Safari 3+ 、Chrome 3+ 、Opera 9+
2、系統的bug
現在穩定版本的bug不少,開發中遇到的問題也不少,例如 treeViewer 會出現遮擋,系統自帶的提示框第一次彈出的時候只能展示一半,等等,當然這些bug都可以通過其他的方式給避免掉.
鄭重提示:想在自己專案中使用rap朋友們一定要慎重考慮
是否有精力和人力來處理一下”小”問題
雖然rap 是rcp的web版本,但是因為 一個是圖形介面,一個是web介面,它們之間還是存在一些差異,現在把我們專案中碰見的差異羅列一下,大家以後碰見了就不至於浪費時間找原因了.^_^
1、 Label和Button按鈕的顯示的差異
當Label 和 Button顯示的文字過長顯示不開的時候,rcp、rap的處理方式就不一樣了,rap顯示不開會自己擷取掉後面的文字,rcp會在文字的中間加上省略號.
還有關於Label 和Button 設定圖片,文字過長的時候rap的圖片不會顯示,可以使用CLabel來替換Label
建議統一處理方式:不使用他們自帶的文字擷取方式,統一計算檔案的長度,在文字最後面加上… 來使二者統一
2、 TreeViewer的差異
目前最新的rap 版本 TreeViewer偶爾有遮擋的問題,這個問題暫時不能有效的避免,
3、 屬性檢視 -tree無法設定行高的問題
關於設定 treeViewer的高度,rcp支援設定行的高度,rap不支援,這個暫時沒有解決方法
4、 對GEF、draw2d的支援
目前rap暫時不支援GEF draw2d,不過目前已經在開發中。
5、 paint監聽,rap沒有
rap組建很多都沒有paint 重繪方法,導致很多介面不能進行重繪。比如treeVIewer上面新增cellEditor進行介面上直接編輯,因為不支援,paint 導致 cellEditor和當前的行的位置不匹配。在後續文章中會對此問題的解決方法進行專門的講解
6、 擴充套件點的差異控制檯擴充套件點
Rap介面目前對擴充套件點的支援也不是很完善,控制檯擴充套件點和幫助檢視擴充套件點都不是很完善,不過大家可以自己實現.不是很複雜
7、 幫助系統的差異
Rcp /rap的幫助系統完全不同,rap的幫助系統只是載入一個jsp頁面
8、上傳下載的差異
一個是web 一個是圖形介面,後續會寫如何在rap實現上傳和下載
9、檢視拖動
用過eclipse的朋友都知道,eclipse之間的檢視是可以任意拖動的,目前Rap不支援,檢視間的拖動,這個算是比較遺憾吧
完成後:
最後點finish。
重新開啟RAP程式,可以看到漢化結果。
1、 在工程的根目錄下面建立一個plugin.properties
資原始檔;
在此資原始檔中寫入需要國際化的內容(鍵/值對),舉例如下:
- helloWorldView_name=eclipse國際化
- helloWorldView_city=北京
- ...
2、
在 MANIFEST.MF檔案中增加程式碼行:Bundle-Localization: plugin;說明:新增程式碼行中的plugin這個名稱是plugin.properties 這個檔案的名稱。也可以是其它的名稱但要與檔名保持一致。
3、 plugin.xml配置檔案對資原始檔進行引用時, 在引用的key前面加一個%即可;
plugin.xml引用資原始檔舉例如下:
Xml程式碼- <extension point="org.eclipse.ui.views">
- <view
- id="org.eclipse.rap.helloworld.helloWorldView"
- class="org.eclipse.rap.helloworld.HelloWorldView"
- name="%helloWorldView_name">
- </view>
- </extension>
類檔案中中文內容的國際化
1、 建立一個messages_zh_CN.properties資原始檔;
說明:此資原始檔中的內容為需要國際化的鍵/值對;
2、 建立一個Messages.java類檔案,檔案內容如下:
Java程式碼- import org.eclipse.osgi.util.NLS;
- public class Messages extends NLS
- {
- private static final String BUNDLE_NAME = "telecomui.nls.messages";
- public static String PriceDomainDao_AddTextToBrower;
- static {
- NLS.initializeMessages(BUNDLE_NAME, Messages.class);
- }
- private Messages(){
- }
- }
3、 在需要進行國際化的類檔案上點選右鍵,出現下圖的操作:
4、 點選藍色選單選項後,彈出下面的窗體,如圖:
5、 在上圖中列出的需要國際化的欄位中,對需要進行國際化的欄位前勾選加號,不需要進行國際化的欄位前勾選差號;
注意:
對於需要進行國際化的欄位項,如圖
中欄位Test_0是生成到資原始檔中的預設key值,建議進行重新命名下,建議命名規則是最好以當前類名做字首;如上欄位可命名為:Test_printStr;
6、 單擊窗體中的【Configure…】按鈕,彈出窗體,如圖:
7、 對上圖中的各選項進行設定後,點選【ok】按鈕—>【next】—>【finish】
先下載以來的打包外掛
war products
輸入下面的地址,選擇相應的外掛
新建一個 war product Configutation嚮導
下面的war product Configutation 配置面板
選擇外掛
將除了 自己專案 在外的所有外掛都刪除,然後點 Add Required Plug-ins 將需要的外掛新增進來,吧所有的關於 Jetty 和Juit 的外掛全部刪除掉,這樣在驗證的時候就不會出現什麼錯誤。避免把沒必要的外掛加進來。
在 Overview 中點 Validate 按鈕驗證是否存在問題:
點選匯出war
釋出成功
1、 Rap樣式原理
Rap的介面樣式目前是以css來配置的,程式啟動後加載相應的css配置檔案再對元件進行樣式設定,介面上的所有元件Label button composit等的樣式最開始都是通過css來確定顯示樣式的.因此只需要選擇不同的css檔案就可以控制css樣式了
2、 Rap如何選擇樣式
Plugin.xml – 擴充套件 – 新建 org.eclipse.rap.ui.branding 擴充套件點 – 在這個擴充套件點上新建branding .在 branding上就可以選擇樣式了,對應的選項是themdID ,點選瀏覽就可以選擇目前已經存在的樣式
目前rap自帶的樣式大約有2個 , Classis 樣式和 Default ,
建議大家使用Classis 樣式,因為預設的default樣式本身在介面顯示上面會有不少的問題.
3、 修改rap自帶的樣式
如果你覺得目前rap介面上的樣式不太符合你的要求,那麼你就可以新建自己的rap樣式 方法如下:
新建擴充套件點:org.eclipse.rap.ui.themes
在擴充套件點上新建 theme
點選 theme 會有一個屬性 file,這個是用來選擇對應的樣式css檔案的.
關於rap每個組建對應的樣式名稱,大家可以通過如下方式進行檢視
找到 org.eclipse.rap.rwt.theme.classes 外掛(直接通過外掛列表-匯出原始碼到目前工程)
在對應的 theme目錄下面就可以看到 classic.css檔案了
4、 Rap樣式的釋出
Rap最好按照預設的方式以外掛的形式進行釋出,這樣就可以鬆耦合了.
1 、解決 rap 字符集亂碼的問題
字符集問題,解決辦法: 在plugin.xml - build.properties 中新增
javacDefaultEncoding.. = UTF-8 即可解決字符集亂碼
2、解決web前臺輸入亂碼問題
使用傳統的 字符集過濾器
寫一個過濾器類
Java程式碼- <span style="font-size: small;">public class CharacterEncodingFilter implements Filter
- {
- private String edcoding;
- private FilterConfig filterConfig;
- private boolean ignore;
- public CharacterEncodingFilter()
- {
- this.edcoding = null;
- this.filterConfig = null;
- this.ignore = true; }
- public void destroy() {
- this.edcoding = null;
- this.filterConfig = null;
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
- {
- if ((this.ignore) || (request.getCharacterEncoding() == null)) {
- String encoding = setCharacterEncoding(request);
- if (encoding != null)
- request.setCharacterEncoding(encoding)
- }
- filterChain.doFilter(request, response);
- }
- public void init(FilterConfig filterConfig) throws ServletException {
- this.filterConfig = filterConfig;
- this.edcoding = filterConfig.getInitParameter("encoding");
- String value = filterConfig.getInitParameter("ignore");
- if (value == null)
- this.ignore = true;
- else if (value.equalsIgnoreCase("true")) {
- this.ignore = true;
- }
- else
- this.ignore = false;
- }
- public String setCharacterEncoding(ServletRequest request)
- {
- return this.edcoding;
- }
- }
- </span>
然後達成 jar 包方式到 war /WEB_INF/lib 目錄下面
在 web.xml 新增
rap 匯出 war 包時,編碼問題處理,加入了字元過濾器的處理方式
1. 在匯出 WAR 後,修改 war 包裡面的 web.xml 檔案,新增過濾器,
內容如下
Java程式碼- <span style="font-size: small;"><span> <filter>
- <filter-name>CharacterEncodingFilter</filter-name>
- <filter-class>com.encoding.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>CharacterEncodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </span>
- </span>
2. 增加 jar 包
把過濾器要用的 jar 包【 encoding.jar 】新增到匯出的 WAR 包的 lib 下面,這樣 WAR 包載入後,能夠找尋到過濾器用到的類。
上面 2 部分可以保證完美的解決中文問題
一 上傳
上傳即將檔案上傳到伺服器上,在客戶端需要寫相應的指令碼,伺服器端需要註冊相應的 handle 接受客戶端的請求。
原理:
Rap 的上傳和下載是通過普通的 web 的方式進行上傳和下載的 , 但是和傳統的 wen 還不相同
1、 rap 本身就單執行緒在跑 , 和上傳下載的執行緒不能進行混淆
所以採用的方式如下:
上傳:通過傳統的方式上傳到指定目錄 ,rap 不能直接操作上傳的檔案流 , 如果想獲得上傳的資料必須要先上傳到指定檔案 , 然後讓 rap 去載入指定檔案即可
下載: 先通過 rap 程式生成需要下載的檔案到指定目錄 , 然後通過 rap 載入成檔案流的形式傳送給客戶端
1 在伺服器端註冊相應的 handler
// 註冊上傳處理事件
IServiceManager manager = RWT.getServiceManager ();
IServiceHandler uploadHandler = new UploadServiceHandler();
manager.registerServiceHandler( "uploadServiceHandler" , uploadHandler); //$NON-NLS-1$
2 在客戶端的指令碼呼叫
目前的做法是建立上傳的 dialog, 在 dialog 裡面新增 browser 控制元件,然後 browser 裡書寫上傳的 javaScript 指令碼,指令碼請求的 url 格式可以通過以下類似的程式碼建立:
private String createUploadUrl(String uploadFileName) {
StringBuffer url = new StringBuffer();
url.append(RWT.getRequest ().getContextPath());
url.append(RWT.getRequest ().getServletPath());
url.append( "?" ); //$NON-NLS-1$
url.append(IServiceHandler . REQUEST_PARAM );
url.append( "=uploadServiceHandler" ); //$NON-NLS-1$
url.append( "&fileName=" ); //$NON-NLS-1$
url.append(uploadFileName);
return url.toString();
}
3 伺服器端 handler 的寫法
public class UploadServiceHandler implements IServiceHandler {
public void service() throws IOException, ServletException {
HttpServletRequest request = RWT.getRequest ();
request.setCharacterEncoding( "UTF-8" );
String fileName = request.getParameter( "fileName" );
FileOutputStream o = null ;
BufferedReader bufferReader = null ;
InputStream in = null ;
try {
in = request.getInputStream();
File f = null ;
try {
f = new File(FileUtil.getTempFilePathAndName (RWT.getRequest ()
.getSession().getAttribute( "userName" ).toString(),
fileName));
} catch (Exception e) {
throw new IOException(e);
}
o = new FileOutputStream(f);
bufferReader = new BufferedReader( new InputStreamReader(in));
String line = null ;
boolean beginWrite = false ;
boolean endWrite = false ;
while ((line = bufferReader.readLine()) != null ) {
if (line.indexOf(PriceDomainBean. class .getName()) != -1) {
if (!beginWrite) {
beginWrite = true ;
} else {
endWrite = true ;
}
}
if (beginWrite) {
o.write((line + "\r\n" ).getBytes());
}
if (endWrite) {
break ;
}
}
} catch (IOException e) {
throw e;
} finally {
if ( null != o) {
o.close();
}
in.close();
if ( null != bufferReader) {
bufferReader.close();
}
}
HttpServletResponse response = RWT.getResponse ();
response.setContentType( "text/html;charset=UTF-8" );
response.getWriter().write(
"<br><br><br><DIV align=center><h2> 上傳成功 !</h2>" );
}
}
二 下載
下載和上傳採用的方式基本相同,只不過是將伺服器檔案讀取到本地,和上傳是一個相反的過程。
1 在伺服器端註冊相應的 handler
// 註冊下載處理事件
IServiceManager manager = RWT.getServiceManager ();
IServiceHandler downloadHandler = new DownloadServiceHandler();
manager.registerServiceHandler( "downloadServiceHandler" , downloadHandler);
2 在客戶端節本的呼叫
在 bowser 控制元件中書寫 js 請求指令碼,指令碼請求的 url 如下
private String createDownloadUrl(String fileName) {
StringBuffer url = new StringBuffer();
url.append (RWT.getRequest ().getContextPath());
url.append (RWT.getRequest ().getServletPath());
url.append ( "?" );
url.append (IServiceHandler. REQUEST_PARAM );
url.append ( "=downloadServiceHandler" );
url.append ( "&fileName='+encodeURI('" );
url.append (fileName);
url.append ( "')" );
return url.toString();
}
3 伺服器端 handler 的寫法
public class DownloadServiceHandler implements IServiceHandler {
public void service() throws IOException, ServletException {
String fileName = URLDecoder.decode (
RWT.getRequest ().getParameter( "fileName" ), "UTF-8" );
String filePathAndName = null ;
try {
filePathAndName = FileUtil
.getTempFilePathAndName (RWT.getRequest ().getSession()
.getAttribute( "userName" ).toString(), fileName);
} catch (Exception e) {
throw new IOException(e);
}
File file = new File(filePathAndName);
if (!file.exists()) {
return ;
}
HttpServletResponse response = RWT.getResponse ();
response.setHeader( "pragma" , "no-cache" );
response.setHeader( "cache-control" , "no-cache" );
response.setDateHeader( "Expires" , 0);
response.setCharacterEncoding( "UTF-8" );
response.setContentType( "text/html;charset=UTF-8" );
response.setHeader( "Content-Disposition" , "attachment;filename="
+ new String(fileName.getBytes( "gb2312" ), "ISO8859-1" ));
try {
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(filePathAndName));
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
byte [] temp = new byte [1024];
int size = 0;
while ((size = in.read(temp)) != -1) {
out.write(temp, 0, size);
}
in.close();
byte [] content = out.toByteArray();
response.setContentLength(content. length );
response.getOutputStream().write(content);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
try {
FileUtil.deleteTempFile (RWT.getRequest ().getSession()
.getAttribute( "userName" ).toString(), fileName);
} catch (Exception e) {
throw new IOException(e);
}
}
}
}
平常進行 rap 程式開發一般都是在 win 下面完成 , 然後在 tomcat 下面測試 , 但是程式最終釋出一般都是在linux aix 上面 , 這個時候就有能會出現一下問題,下面 2 個問題是我們把在開發中真是出現的問題,與大家一起分享下 ;
1、 圖片路徑
這個是最常用的方法,就是在 rap 中載入圖片進行顯示,剛開始我們使用的是如下程式碼
Java程式碼- public static String getRoot() {
- String path = null ;
- try {
- path = FileLocator.toFileURL (Platform.getBundle (Activator. PLUGIN_ID ).getEntry( "" )).getPath();
- path = path.substring(path.indexOf( "/" ) + 1, path.length());
- }
- catch (Exception e) {
- log .error( "getRoot method :" , e);
- }
- return path;
- }
來獲得系統的跟路徑 . 但是當程式在移植到 linux 和 aix 上面的時候發現圖片路徑全部失效 . 可以使用如下方式進行定址來獲得圖片。
Java程式碼- /**
- * 獲取圖片
- * @param fileName
- * 圖片的名稱
- * @return 先從快取物件中查詢,若有直接返回,若沒有,則將圖片載入到快取中,在從快取中將圖片傳給呼叫著
- */
- public static Image getImage(String fileName) {
- Bundle bundle = Platform.getBundle ( "TelecomUI" );
- URL url = bundle.getEntry( "icons" );
- try {
- url = Platform.asLocalURL (url) ;
- }
- catch (Exception e) {
- }
- Image image = registry .get(fileName);
- if ( null != image) {
- return image;
- }
- else {
- URL fullPathString = bundle.getEntry( "icons/" + fileName);
- ImageDescriptor des = ImageDescriptor.createFromURL (fullPathString);
- registry .put(fileName, des);
- return ImageDescriptor.createFromURL (fullPathString).createImage();
- }
- }
2、 獲得螢幕的解析度
因為需要把一些彈出的元件居中顯示 , 這個時候就需要獲得系統的解析度
剛開始我們使用的方法如下;
Java程式碼- screenH = Toolkit.getDefaultToolkit().getScreenSize ().height;
- screenW = Toolkit.getDefaultToolkit().getScreenSize ().width;
上面這段程式碼在 window 下面是沒有問題的 , 可是到了 linux 和 aix 下面就報錯了 , 找不到
sun.awt.X11.XToolkit 類
可以採用如下方式來獲得螢幕的解析度
Java程式碼- DisPlay.getDefault().getClientArea();
3、 其他如果有發現不相容會陸續補充