最近上司讓研究GWT,學習這個AJAX架構,看看對我們的系統能否內建進去,花了1周多時間研究啊。照葫蘆畫瓢,做了一個Demo,對這個架構也有了一個初步的認識, 感覺就是一個Web的 SWT。
GWT 學習筆記
一、使用者界面(User Interface)
在開發GWT應用程式時要注意一件事,它的開發非常像Swing,SWT,甚至是VB。你建立按鈕、清單、表單,經事件監聽器與之互動。你進行布局,試着讓它們在一定的螢幕分辨率和螢幕尺寸下看起來更好看。主要的不同之處在于GWT應用是顯示在Web浏覽器上,它涉及到HTML頁面。
傳統的Web應用程式是以一系列的HTML頁面為結構,并以之導航的。
1、與HTML的聯系
每一個GWT應用程式裡面都存在一個HTML頁面,它可以是一個靜态的頁面,或者是伺服器端的頁面如JSP,Struts,Ruby on Rails等。
實際上,MyApp.html存在于public目錄,意味着它将被一字不差的複制到最終的伺服器端的部署區域,如果頁面還連結着圖檔,樣式表等,它們也都會複制到同樣的目錄下。
在HTML頁面代碼的頂部需要meta标簽來聯系GWT元件。
如:<meta name='gwt:module' content='com.xyz.MyApp'>
一個GWT元件是一個用戶端代碼和資源的集合。名為com.mycompany.MyApp的元件被定義到元件檔案src/com/mycompany/MyApp.gwt.xml中。
-----------------------------------------------
<module>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Specify the app entry point class. -->
<entry-point class='com.xyz.client.MyApp'/>
</module>
-----------------------------------------------
在邏輯上,當HTML頁面載入後,GWT檢視meta标簽,讀入xml檔案并得到類名,在入口點類中開始調用代碼。
2、入口點(Entry Point)
入口點類(MyApp)擴充了入口點接口并提供了一個方法onModuleLoad()。這個方法在構造GWT應用程式使用者界面中起作用。
MyProject/src/com/xyz/client/MyApp.java
-----------------------------------------------
public void onModuleLoad(){
final Button button=new Button("Click me");
final Label label=new Label();
//...
-----------------------------------------------
上面的腳手架代碼建立了兩個GWT使用者界面元素,一個按鈕和一個标簽。
如果要追溯和浏覽HTML檔案,其代碼底部有兩個占位符用于動态内容:
MyProject/src/com/xyz/public/MyApp.html
-----------------------------------------------
<table align=center>
<tr>
<td id="slot1"></td>
<td id="slot2"></td>
</tr>
</table>
-----------------------------------------------
Java代碼将通過“id=”來引用并填充你建立的按鈕和标簽。
MyProject/src/com/xyz/client/MyApp.java
-----------------------------------------------
RootPanel.get("slot1").add(button);
RootPanel.get("slot2").add(label);
-----------------------------------------------
上面的表定義了怎樣使用小部件在螢幕上布局。
還有一個更好的布局方式是使用面闆(Panel)。GWT面闆也是一個小部件,它能容納一個或多個小部件并以特殊的方式管理它們。
例如:你可以建立一個水準面闆(Horizontal Panel),在其上加入按鈕和标簽,再把該面闆加入到RootPanel的頁面。即:
RootPanel.get().add(hPanel);
3、事件(Event)
繼續看剛才的例子,讓按鈕做一些事:
MyProject/src/com/xyz/client/MyApp.java
-----------------------------------------------
button.addClickListener(new ClickListener(){
public void onClick(Widget sender){
...
-----------------------------------------------
4、小部件(Widget)
(1)檔案上傳 FileUpload
設定輸入element的名字并使用setName()方法送出到伺服器。
(2)伸縮表和網格 FlexTable and Grid
表可以包含文本、HTML和其它任意小部件。網格總是相同的,有固定的尺寸。
使用setText(),setHTML(),setWidget()方法來加入單元項,使用getCellFormatter()來定制單元的外觀。單元可以跨越多行或多列。
(3)Frame and NamedFrame
此部件包裹在HTML的<iframe>元素中,包含于任意Web站點。
使用setUrl()方法來設定web頁面位址。
其CSS風格:.gwt-Frame{}
(4)HTML
其CSS風格:.gwt-HTML{}
(5)Image
在給定的URL顯示圖檔的小部件。
使用setUrl()方法來設定圖檔位址。使用addLoadListener()方法來檢測圖檔是否載入或是否有錯。
其CSS風格:.gwt-Image{}
(6)Hyperlink
其CSS風格:.gwt-Hyperlink{}
(7)Label
其CSS風格:.gwt-Label{}
(8)ListBox
其CSS風格:.gwt-ListBox{}
(9)MenuBar and MenuItem
使用addItem()來加入菜單欄。
其CSS風格:
.gwt-MenuBar{the menu bar itself}
.gwt-MenuItem{menu items}
.gwt-MenuItem-selected{selected menu items}
.gwt-Frame{}
(10)PasswordTextBox
其CSS風格:.gwt-PasswordTextBox{}
(11)TabBar
常用于Tab面闆的一部分。調用addTab()方法來加入TarBar,調用addTabListener()來檢測聚焦前(onBeforeTabSelected())和聚集後(onTabSelected())tab被選擇的情況。
其CSS風格:
.gwt-TarBar{the tab bar itself}
.gwt-TarBarFirst{the left side spacer of the bar}
.gwt-TabBarRest{the right side spacer of the bar}
.gwt-TabBarItem{tabs}
.gwt-TabBarItem-selected{additional style for selected tabs}
(12)TextArea
文本域允許顯示鍵入的多行文本。使用setCharacterWidth()和setVisibleLines()方法來設定文本域的尺寸。
調用getCursorPos(),getSelectionLength(),getSelectedText()來檢測目前所選擇的文本。
調用addKeyBoardListener()方法來監測按鍵情況。
其CSS風格:.gwt-TextArea{}
(13)TextBox
文本框顯示鍵入的單行文本。
設定文本框的尺寸用setVisibleLength()方法。
調用getCursorPos(),getSelectionLength(),getSelectedText()來檢測目前所選擇的文本。
調用addKeyBoardListener()方法來監測按鍵情況。
(14)Tree and TreeItem
調用addItem()方法增加樹子項。
其CSS風格:
.gwt-Tree{the tree itself}
.gwt-TreeItem{a tree item}
.gwt-TreeItem-selected{a selected tree item}
5、面闆(Panel)
面闆是一個包含多個小部件的部件。面闆也可以包含其它面闆。可以使用它在網格下、層面下(deck)、行、列下布局小部件。
(1)AbsolutePanel
允許面闆重疊。
(2)DeckPanel
所有的小部件在一個“deck”中,一次隻能顯示一個小部件。
用add()方法加入小部件,用showWidget()顯示某個小部件。
(3)DockPanel
面闆的中央部分是保留部分,四周東西南北均可添加部件。
(4)FlowPanel
使用預設的HTML布局。
(5)FocusPanel
(6)FormPanel
面闆的内容對應HTML的<form>元素。
FormPanel隻包含如下元素:TextBox,PasswordTextBox,RadioButton,CheckBox,TextArea,ListBox,FileUpload。
使用setName()方法把各元素名和表單域聯系在一起,傳遞到伺服器。
使用setAction()方法設定URL用于送出表單。submit()實際送出表單。
調用addFormHandler()檢測表單是否已送出。
(7)HorizontalPanel
(8)HTMLPanel
(9)PopupPanel
popup面闆可以彈出其它小部件。
(10)ScrollPanel
面闆的内容可卷。使用構造器或者setWidget()函數定義包裹的部件。
(11)StackPanel
垂直的方式顯示,類似于子菜單。
(12)TabPanel
(13)VerticalPanel
功能介紹(Web控件)
Web控件是GWT表示層的核心,通過使用GWT提供的Web控件可以建立豐富的用戶端畫面。
GWT的web元件主要分為兩類:輸入控件和Layout控件。
輸入控件主要是指向伺服器送出資料,處理操作,顯示伺服器資料的空間,主要包括:
Button,RadionButton,PushButton,ToggleButton,CheckBox,TextBox,PasswordBox
TextArea, HyperLink,ListBox,MenuBar,Tree,Table, TabBar,DialogBox, PopupPanel
RichTextArea, DisclosurePanel, SuggestBox
Layout 空間主要用來有規律地放置輸入控件,主要包括:
Stackpanel,HorizontalPanel,VerticalPanel, FlowPanel, VerticalSplitPanel, HorizontalSplitPanel,
DockPanel, TabPanel,
事件模型(Event & Listener)
如何處理頁面的事件呢,例如點選按鈕,Textbox失去焦點等?
如下的代碼建立了一個按鈕,按鈕的名字是“Click Me”,當你點選這個按鈕的時候處罰一個Click事件,Click事件觸發onClick(Widget sender)方法。
sender表示觸發onClick方法的元件,這裡隻是簡單的在頁面上顯示Hello World資訊(相當于#的alert方法)。
常見的Listener如下:
ChangeListener
ClickListener
FocusListener
KeyboardListener
MouseListener
MouseWheelListener
PopupListener
ScrollListener
TableListener
TreeListener
功能介紹(遠過程調用RPC)
體系結構
GWT應用中頁面一旦加載,就再也不會向伺服器請求HTML内容,所有的畫面遷移,轉換都在用戶端進行,但是資料還是會向伺服器送出,或者從伺服器擷取。
伺服器上負責處理資料的對象在GWT中叫做Service,每個Service有三個類組成:服務方法定義接口(Service),異步調用接口(ServiceAsync)和伺服器方法實作類ServiceImpl。
以Login為例子說明:
遠端調用
獲得伺服器方法的調用接口(skeleton)。
設定服務位置。
遠端調用伺服器上的方法,注意這裡是異步調用,在和調用之前代碼可能先被調用了。
調用出錯,或者調用方法抛出異常的時候調用的方法。
調用成功傳回時候調用的方法。
參數和傳回值系列化類型
這裡的參數指的是Service方法調用的參數和傳回值。
由于GWT的用戶端代碼都是#,而伺服器代碼都是使用Java編寫的,這就涉及到#調用Java方法的時候
如何傳遞參數,如何取得傳回值的問題。
可序列化的類型包括:
(1) 原始類型,例如:char, byte, short, int, long, boolean, float, double;
(2) String,java.util.Date,或者原始類型的包裝類型,例如: Character, Byte, Short, Integer, Long, Boolean, Float, or Double;
(3) 可序列化類型數組(包含(4)和(5)定義的類型)
(4) 使用者定義的可序列化類型
(5) 該類型至少有一個可序列化的子類型
針對上述(4)中說明的,什麼是使用者自定義的可序列化類型呢?必須滿足以下亮點:
第一,必須直接或者間接(例如,父類型實作了這個接口)的實作了IsSerializable接口
第二,所有非transient類型都是可序列化的(final類型的屬性在GWT中被視為transient類型)
是否支援容器類型呢?那麼又如何聲明呢?
支援容器類型,GWT可以使用Type 參數來表示容器類型内部的元素的類型,例如:
注意GWT暫時不支援使用 JDK 5.0 的模闆容器
//使用者自定義序列化類型
public class MyClass implements IsSerializable {
public Set setOfStrings;
public Map mapOfStringToString;
}
// 伺服器方法實作類
public interface MyService extends RemoteService {
List reverseListAndConvertToStrings(List c);
}