用Eclipse RCP & ECF 實(shí)現(xiàn) Google Talk客戶端
Posted on 2006-10-09 19:07 Dart 閱讀(2004) 評(píng)論(2) 編輯 收藏 所屬分類: ECF大家用過(guò)Google Talk嗎?它是Google推出的一個(gè)IM,通訊協(xié)議是我們熟悉的Jabber協(xié)議。我通過(guò)這篇文章給大家簡(jiǎn)單介紹一下如何利用ECF實(shí)現(xiàn)一個(gè)Google Talk客戶端。源代碼下載:http://www.aygfsteel.com/Files/reloadcn/Chat.rar
1.準(zhǔn)備工作
先下載ECF:
www.eclipse.org/ecf
為了能夠測(cè)試我們這個(gè)客戶端是否能正常運(yùn)行,我們還需要下載一個(gè)Goolge Talk客戶端:www.google.com/talk
當(dāng)然,我們想要登陸Google的服務(wù)器必須擁有一個(gè)GoogleMail帳號(hào),由于現(xiàn)在GoogleMail帳號(hào)不是隨便申請(qǐng)的,需要GoogleMail用戶推薦才能申請(qǐng),但也能通過(guò)一些網(wǎng)站進(jìn)入GoogleMail申請(qǐng)頁(yè)面,大家可以上網(wǎng)搜索一下,我在這里就不多說(shuō)了。
我們要建立一個(gè)Google Talk的客戶端,需要了解一些ECF的知識(shí)。大家可以去Eclipse主站獲得更多的信息。
2.建立一個(gè)RCP Mail? Example
我們先選擇創(chuàng)建Plugin Project,取名為“Chat”,當(dāng)?shù)较驅(qū)ы?yè)的第二頁(yè)的時(shí)候,注意在“Would you to create a rich client platform”選項(xiàng)選擇“yes”,這樣確保你創(chuàng)建的是一個(gè)RCP工程,見(jiàn)下圖:
當(dāng)?shù)阶詈笠豁?yè)的時(shí)候,選擇Mail Template:
完成向?qū)Ш笪覀儗?huì)得到一個(gè)簡(jiǎn)單的RCP工程。
3.登陸的代碼
1)連接前工作
ECF是一個(gè)基于Eclipse的通訊平臺(tái),它其中一部分實(shí)現(xiàn)了Jabber協(xié)議。ECF有一個(gè)ClientContainer概念,其實(shí)就相當(dāng)于一個(gè)維護(hù)客戶端的對(duì)象,它具有連接、斷開(kāi)連接服務(wù)的方法,并且能夠添加一些通訊中的事件監(jiān)聽(tīng)器。所以,我們創(chuàng)建Google Talk客戶端首先就要擁有這么一個(gè)對(duì)象,而且它在整個(gè)程序生命周期中是唯一的。
讓我們修改一下ChatPlugin中的代碼:
首先,我們?cè)谶@個(gè)類里增加一個(gè)私有變量clientContainer,并且給他加上Getter、Setter方法:
OK,試想一下,當(dāng)我們?cè)诘顷慓oogle服務(wù)器的時(shí)候才會(huì)去使用這個(gè)clientContainer去連接服務(wù)器,而且我們登陸的用戶信息是需要保存下來(lái)的,以供后面的代碼訪問(wèn),所以這個(gè)clientContainer的生成方式應(yīng)該是Lazy的,并且我們還需要建立一個(gè)我們登陸帳戶的變量:
ECF中針對(duì)用戶的信息是用ID來(lái)表示的,它是一個(gè)接口,ECF已經(jīng)實(shí)現(xiàn)了一個(gè)XMPPID,正好是我們Jabber帳戶需要的。
clientContainer有一個(gè)connect方法去登陸服務(wù)器,而且在連接后不再具有其他什么動(dòng)作。讀者會(huì)問(wèn):那什么時(shí)候通知我們連接成功呢?并且用戶在服務(wù)器端的好友怎么獲得呢?
clientContainer只負(fù)責(zé)連接,上述的那些事情都屬于在連接服務(wù)器過(guò)程中或者連接后,服務(wù)器反饋給客戶端的信息,這些信息需要我們給clientContainer設(shè)置監(jiān)聽(tīng)器去捕獲。
其中有一個(gè)監(jiān)聽(tīng)器名為ISharedObjectContainerListener,這個(gè)監(jiān)聽(tīng)器能夠捕獲一些在連接過(guò)程和斷開(kāi)連接過(guò)程中的事件,比如SharedObjectConnectedEvent (連接成功事件)、SharedObjectDisconnectedEvent (斷開(kāi)連接成功事件),如果我們需要在客戶端連接上服務(wù)器后做點(diǎn)什么,那這個(gè)監(jiān)聽(tīng)器是必須的。
2)開(kāi)始連接服務(wù)器
我們看看clientContainer有一個(gè)connect方法。
這個(gè)方法需要有兩個(gè)參數(shù):用戶的ID、連接上下文
用戶ID我們剛才已經(jīng)說(shuō)過(guò)了,它是ECF提出的一個(gè)概念,我們可以通過(guò)IDFactory生成它:
大家發(fā)現(xiàn)了嗎,上面代碼中的makeID方法需要兩個(gè)參數(shù),一個(gè)參數(shù)我們可以從clientContainer獲得,它是連接名字空間,我的理解是某種協(xié)議。第二個(gè)是用戶名,這個(gè)參數(shù)在我們這里是Google Talk的帳號(hào),也就是GMail帳號(hào),但是目前我們還沒(méi)有辦法從外部獲得,這我會(huì)在下面的內(nèi)容中提到,到時(shí)候就可以將這個(gè)程序串起來(lái),大家現(xiàn)在可以把它看作已經(jīng)具備某些值。
好,我們已經(jīng)有了ID,現(xiàn)在看看什么如何創(chuàng)建上下文。連接上下文其實(shí)很簡(jiǎn)單,我們可以這樣理解:就是在我們連接的時(shí)候,clientContainer會(huì)向客戶端所取一些相關(guān)的信息,比如nikename,password,這樣理解起來(lái)就不麻煩了,而且在我們的這個(gè)Google Talk客戶端中,它也只會(huì)向我們索取password和username,來(lái)看看我們代碼就更清楚了:
到目前為止,我們已經(jīng)完成了連接這個(gè)環(huán)節(jié),我們將這些代碼都封裝到ChatPlugin的login方法中,到時(shí)候通過(guò)外部的操作好調(diào)用。
1.準(zhǔn)備工作
先下載ECF:
www.eclipse.org/ecf
為了能夠測(cè)試我們這個(gè)客戶端是否能正常運(yùn)行,我們還需要下載一個(gè)Goolge Talk客戶端:www.google.com/talk
當(dāng)然,我們想要登陸Google的服務(wù)器必須擁有一個(gè)GoogleMail帳號(hào),由于現(xiàn)在GoogleMail帳號(hào)不是隨便申請(qǐng)的,需要GoogleMail用戶推薦才能申請(qǐng),但也能通過(guò)一些網(wǎng)站進(jìn)入GoogleMail申請(qǐng)頁(yè)面,大家可以上網(wǎng)搜索一下,我在這里就不多說(shuō)了。
我們要建立一個(gè)Google Talk的客戶端,需要了解一些ECF的知識(shí)。大家可以去Eclipse主站獲得更多的信息。
2.建立一個(gè)RCP Mail? Example
我們先選擇創(chuàng)建Plugin Project,取名為“Chat”,當(dāng)?shù)较驅(qū)ы?yè)的第二頁(yè)的時(shí)候,注意在“Would you to create a rich client platform”選項(xiàng)選擇“yes”,這樣確保你創(chuàng)建的是一個(gè)RCP工程,見(jiàn)下圖:
當(dāng)?shù)阶詈笠豁?yè)的時(shí)候,選擇Mail Template:
完成向?qū)Ш笪覀儗?huì)得到一個(gè)簡(jiǎn)單的RCP工程。
3.登陸的代碼
1)連接前工作
ECF是一個(gè)基于Eclipse的通訊平臺(tái),它其中一部分實(shí)現(xiàn)了Jabber協(xié)議。ECF有一個(gè)ClientContainer概念,其實(shí)就相當(dāng)于一個(gè)維護(hù)客戶端的對(duì)象,它具有連接、斷開(kāi)連接服務(wù)的方法,并且能夠添加一些通訊中的事件監(jiān)聽(tīng)器。所以,我們創(chuàng)建Google Talk客戶端首先就要擁有這么一個(gè)對(duì)象,而且它在整個(gè)程序生命周期中是唯一的。
讓我們修改一下ChatPlugin中的代碼:
首先,我們?cè)谶@個(gè)類里增加一個(gè)私有變量clientContainer,并且給他加上Getter、Setter方法:
XMPPClientSOContainer?clientContainer;
????public?XMPPClientSOContainer?getClientContainer()?{
????????return?clientContainer;
????}
????public?void?setClientContainer(XMPPClientSOContainer?clientContainer)?{
????????this.clientContainer?=?clientContainer;
????}
????public?XMPPClientSOContainer?getClientContainer()?{
????????return?clientContainer;
????}
????public?void?setClientContainer(XMPPClientSOContainer?clientContainer)?{
????????this.clientContainer?=?clientContainer;
????}
OK,試想一下,當(dāng)我們?cè)诘顷慓oogle服務(wù)器的時(shí)候才會(huì)去使用這個(gè)clientContainer去連接服務(wù)器,而且我們登陸的用戶信息是需要保存下來(lái)的,以供后面的代碼訪問(wèn),所以這個(gè)clientContainer的生成方式應(yīng)該是Lazy的,并且我們還需要建立一個(gè)我們登陸帳戶的變量:
????private?ID?userID;
????public?ID?getUserID()?{
????????return?userID;
????}
????public?void?setUserID(ID?userID)?{
????????this.userID?=?userID;
????}
????public?ID?getUserID()?{
????????return?userID;
????}
????public?void?setUserID(ID?userID)?{
????????this.userID?=?userID;
????}
ECF中針對(duì)用戶的信息是用ID來(lái)表示的,它是一個(gè)接口,ECF已經(jīng)實(shí)現(xiàn)了一個(gè)XMPPID,正好是我們Jabber帳戶需要的。
clientContainer有一個(gè)connect方法去登陸服務(wù)器,而且在連接后不再具有其他什么動(dòng)作。讀者會(huì)問(wèn):那什么時(shí)候通知我們連接成功呢?并且用戶在服務(wù)器端的好友怎么獲得呢?
clientContainer只負(fù)責(zé)連接,上述的那些事情都屬于在連接服務(wù)器過(guò)程中或者連接后,服務(wù)器反饋給客戶端的信息,這些信息需要我們給clientContainer設(shè)置監(jiān)聽(tīng)器去捕獲。
其中有一個(gè)監(jiān)聽(tīng)器名為ISharedObjectContainerListener,這個(gè)監(jiān)聽(tīng)器能夠捕獲一些在連接過(guò)程和斷開(kāi)連接過(guò)程中的事件,比如SharedObjectConnectedEvent (連接成功事件)、SharedObjectDisconnectedEvent (斷開(kāi)連接成功事件),如果我們需要在客戶端連接上服務(wù)器后做點(diǎn)什么,那這個(gè)監(jiān)聽(tīng)器是必須的。
clientContainer.addListener(
??????????????????new?ISharedObjectContainerListener()?{
???????????????????public?void?handleEvent(IContainerEvent?evt)?
???????????????????????if?(evt?instanceof?ISharedObjectContainerConnectedEvent)?{
?????????????????????????????????// 連接服務(wù)器成功后做點(diǎn)什么呢?
??????????????????????? }
???????????????????????if?(evt?instanceof?ISharedObjectContainerDisconnectedEvent)?{
???????????????????????????????? // 斷開(kāi)服務(wù)器成功后做點(diǎn)什么呢?
????????????????????????}
???????????????????}
???????????????????},?null);
2)開(kāi)始連接服務(wù)器
我們看看clientContainer有一個(gè)connect方法。
這個(gè)方法需要有兩個(gè)參數(shù):用戶的ID、連接上下文
用戶ID我們剛才已經(jīng)說(shuō)過(guò)了,它是ECF提出的一個(gè)概念,我們可以通過(guò)IDFactory生成它:
userID?=?IDFactory.getDefault().makeID(
????????????????????????????????????????clientContainer.getConnectNamespace(),
????????????????????????????????????????getUserName());
????????????????????????????????????????clientContainer.getConnectNamespace(),
????????????????????????????????????????getUserName());
大家發(fā)現(xiàn)了嗎,上面代碼中的makeID方法需要兩個(gè)參數(shù),一個(gè)參數(shù)我們可以從clientContainer獲得,它是連接名字空間,我的理解是某種協(xié)議。第二個(gè)是用戶名,這個(gè)參數(shù)在我們這里是Google Talk的帳號(hào),也就是GMail帳號(hào),但是目前我們還沒(méi)有辦法從外部獲得,這我會(huì)在下面的內(nèi)容中提到,到時(shí)候就可以將這個(gè)程序串起來(lái),大家現(xiàn)在可以把它看作已經(jīng)具備某些值。
好,我們已經(jīng)有了ID,現(xiàn)在看看什么如何創(chuàng)建上下文。連接上下文其實(shí)很簡(jiǎn)單,我們可以這樣理解:就是在我們連接的時(shí)候,clientContainer會(huì)向客戶端所取一些相關(guān)的信息,比如nikename,password,這樣理解起來(lái)就不麻煩了,而且在我們的這個(gè)Google Talk客戶端中,它也只會(huì)向我們索取password和username,來(lái)看看我們代碼就更清楚了:
clientContainer.connect(userID,?new?IConnectContext()?{
???????????public?CallbackHandler?getCallbackHandler()?{
???????????????return?new?CallbackHandler()?{????
?????????????????????public?void?handle(?Callback[]?callbacks)throws?IOException,
????????????????????????????????????????????????????????UnsupportedCallbackException?{
?????????????????????????????if?(callbacks?==?null)return;
???????????????????????????????for?(int?i?=?0;?i?<?callbacks.length;?i++)?{
?????????????????????????????????????if?(callbacks[i]?instanceof?NameCallback)?{
??????????????????????????????????????NameCallback?ncb?=?(NameCallback)?callbacks[i];
??????????????????????????????????????ncb.setName(getUserName());
??????????????????????????????????????}?else?
????????????????????????????? if?(callbacks[i]?instanceof?ObjectCallback)?{
?????????????????????????????????ObjectCallback?ocb?=?(ObjectCallback)?callbacks[i];
??????????????????????????????????ocb.setObject(password);
?????????????????????????????????}
????????????????????????????????????????????????????????}
????????????????????????????????????????????????????}
????????????????????????????????????????????????};
????????????????????????????????????????????}
????????????????????????????????????????});
???????????public?CallbackHandler?getCallbackHandler()?{
???????????????return?new?CallbackHandler()?{????
?????????????????????public?void?handle(?Callback[]?callbacks)throws?IOException,
????????????????????????????????????????????????????????UnsupportedCallbackException?{
?????????????????????????????if?(callbacks?==?null)return;
???????????????????????????????for?(int?i?=?0;?i?<?callbacks.length;?i++)?{
?????????????????????????????????????if?(callbacks[i]?instanceof?NameCallback)?{
??????????????????????????????????????NameCallback?ncb?=?(NameCallback)?callbacks[i];
??????????????????????????????????????ncb.setName(getUserName());
??????????????????????????????????????}?else?
????????????????????????????? if?(callbacks[i]?instanceof?ObjectCallback)?{
?????????????????????????????????ObjectCallback?ocb?=?(ObjectCallback)?callbacks[i];
??????????????????????????????????ocb.setObject(password);
?????????????????????????????????}
????????????????????????????????????????????????????????}
????????????????????????????????????????????????????}
????????????????????????????????????????????????};
????????????????????????????????????????????}
????????????????????????????????????????});
到目前為止,我們已經(jīng)完成了連接這個(gè)環(huán)節(jié),我們將這些代碼都封裝到ChatPlugin的login方法中,到時(shí)候通過(guò)外部的操作好調(diào)用。
4.開(kāi)始登陸
我們利用SWT Dialog建立一個(gè)簡(jiǎn)單的登陸對(duì)話框:
這個(gè)類需要有幾個(gè)屬性:用戶帳號(hào)、用戶密碼、對(duì)話框返回值。
當(dāng)我們點(diǎn)擊了Login后,對(duì)話框關(guān)閉,并將文本中的值賦給帳號(hào)和密碼這兩個(gè)屬性,返回值設(shè)為SWT.OK;如果是Cancel的話那我們就直接關(guān)閉對(duì)話框,返回值設(shè)置為SWT.CANCEL。
我們?cè)俚組ail RCP中提供的MessagePopupAction類中修改它的run方法:
?public?void?run()?{
????????if(ChatPlugin.getDefault().getClientContainer()?!=?null)?{
????????????MessageDialog.openInformation(window.getShell(),"Info","已經(jīng)登陸了,請(qǐng)先注銷再重新登陸");
????????????return;
????????}
????????LoginDialog?dialog?=?new?LoginDialog(window.getShell(),SWT.NONE);
????????dialog.open();
????????if(dialog.getDialogResult()?==?SWT.OK){
????????????ChatPlugin.getDefault().setPassword(dialog.getPassword());
????????????ChatPlugin.getDefault().setUserName(dialog.getUser());
????????????ChatPlugin.getDefault().login();
????????}
????}
????????if(ChatPlugin.getDefault().getClientContainer()?!=?null)?{
????????????MessageDialog.openInformation(window.getShell(),"Info","已經(jīng)登陸了,請(qǐng)先注銷再重新登陸");
????????????return;
????????}
????????LoginDialog?dialog?=?new?LoginDialog(window.getShell(),SWT.NONE);
????????dialog.open();
????????if(dialog.getDialogResult()?==?SWT.OK){
????????????ChatPlugin.getDefault().setPassword(dialog.getPassword());
????????????ChatPlugin.getDefault().setUserName(dialog.getUser());
????????????ChatPlugin.getDefault().login();
????????}
????}
代碼邏輯很清楚。當(dāng)我們點(diǎn)擊這個(gè)按鈕的時(shí)候,就會(huì)彈出登陸的對(duì)話框,然后我們輸入信息后就可以正常登陸了。
注意后面的代碼,我們將ChatPlugin中的用戶名和密碼先設(shè)置好后再調(diào)用登陸方法。如果登陸失敗的話會(huì)在ChatPlugin的login方法中捕獲到連接失敗的異常。
5.獲得我的好友們
怎么去獲得我的好友呢?
剛才已經(jīng)在前面提到了一點(diǎn):clientContainer只負(fù)責(zé)去連接,而那些網(wǎng)絡(luò)的事件需要我們?nèi)ピ黾颖O(jiān)聽(tīng)器捕獲。獲得好友也是一樣的,我簡(jiǎn)單說(shuō)一下。
clientContainer可以通過(guò)getAdapter去獲得一個(gè)IPresenceContainer類型對(duì)象,這個(gè)對(duì)象可以增加監(jiān)聽(tīng)獲得好友信息的監(jiān)聽(tīng)器,不僅如此,它還可以獲得消息發(fā)送對(duì)象和消息的監(jiān)聽(tīng)對(duì)象,這我會(huì)在后面介紹。
我們要想獲得好友信息,就應(yīng)該通過(guò)clientContainer獲得IPresenceContainer對(duì)象,然后給它增加一個(gè)能夠獲得好友事件的監(jiān)聽(tīng)器。
問(wèn)題在這里,我們應(yīng)該在什么時(shí)候去獲得這個(gè)對(duì)象呢?那這個(gè)監(jiān)聽(tīng)器接口是不是需要一些現(xiàn)有類去實(shí)現(xiàn)呢?
先說(shuō)第一個(gè)問(wèn)題:我們什么時(shí)候去獲得這個(gè)對(duì)象,并為它增加監(jiān)聽(tīng)器
一般情況下,我們?cè)诘顷懗晒σ郧暗臅r(shí)候是不會(huì)去捕獲我們的好友列表的消息的,而且也捕獲不到,服務(wù)器在沒(méi)有驗(yàn)證我們的客戶端時(shí),是不會(huì)發(fā)過(guò)來(lái)的,所以我們需要在登陸成功后去獲得這個(gè)對(duì)象,并為它增加一個(gè)監(jiān)聽(tīng)去。而這個(gè)對(duì)象也是需要作為一個(gè)私有變量存放起來(lái),供其他類去訪問(wèn)。所以我們需要在第3節(jié)中提到了監(jiān)聽(tīng)登陸成功的方法中寫(xiě)這段代碼,由于篇幅問(wèn)題,我不在這里給出代碼片段,讀者可以去看源代碼。
看看第二個(gè)問(wèn)題:誰(shuí)需要實(shí)現(xiàn)這個(gè)監(jiān)聽(tīng)器?
我們常見(jiàn)的IM中,都是有一個(gè)列表控件保存我們當(dāng)前的用戶信息的,所以我們?cè)讷@得好友列表后就需要往某些Viewer中增加一些內(nèi)容,來(lái)表示這是我們的好友列表。
我在這個(gè)客戶端中,采用了一個(gè)View作為顯示好友列表的控件,該View名為SimpleView,這個(gè)View具有一個(gè)TableViewer。該類的具體生成方法我不在多說(shuō),大家可以看看源代碼,我只說(shuō)一下這個(gè)View如何去實(shí)現(xiàn)監(jiān)聽(tīng)獲得好友信息的事件的。
我們讓它實(shí)現(xiàn)IPresenceListener接口,并修改handleSetRosterEntry方法:
public?void?handleSetRosterEntry(IRosterEntry?entry)?{
????????final?IRosterEntry?e1?=?entry;
????????Display.getDefault().asyncExec(new?Runnable()?{
????????????public?void?run()?{
????????????????if(e1.getInterestType()?==InterestType.BOTH){
????????????????roseters.add(e1);
????????????????if(viewer.getInput()?!=?roseters)?viewer.setInput(roseters);
????????????????viewer.refresh();
????????????????}
????????????}
????????});
????}
????????final?IRosterEntry?e1?=?entry;
????????Display.getDefault().asyncExec(new?Runnable()?{
????????????public?void?run()?{
????????????????if(e1.getInterestType()?==InterestType.BOTH){
????????????????roseters.add(e1);
????????????????if(viewer.getInput()?!=?roseters)?viewer.setInput(roseters);
????????????????viewer.refresh();
????????????????}
????????????}
????????});
????}
這個(gè)方法就是截獲獲得好友信息的接口函數(shù),entry表示的是從服務(wù)器獲得的一些和客戶端好友有關(guān)的信息,每當(dāng)獲得一個(gè),判斷一下這個(gè)好友是否都在雙方的好友名單中,如果不是那就不要增加它;反之,我們就會(huì)把這個(gè)entry放到一個(gè)名位roseters的List對(duì)象中,然后刷新viewer。這里的viewer是剛才我們提到的TableViewer,做過(guò)SWT/JFace的讀者一定知道,這個(gè)類需要我們?nèi)樗砑觾蓚€(gè)接口實(shí)現(xiàn),一個(gè)是ContentProvider接口,一個(gè)是LabelProvier接口,這兩個(gè)接口代碼讀者可以看看我的源碼,這里就不寫(xiě)了。如果您對(duì)SWT/JFace不熟悉的話也沒(méi)關(guān)系,這方面的資料很多。
看看我們登陸后獲得好友列表是什么樣的:
6.監(jiān)聽(tīng)消息
有了剛才增加好友的經(jīng)驗(yàn),我們現(xiàn)在就很容易解決這個(gè)問(wèn)題。
同樣,監(jiān)聽(tīng)消息還是由IPresenceContainer對(duì)象增加的監(jiān)聽(tīng)器來(lái)截獲的。
而我讓我們工程中一個(gè)名為View的類實(shí)現(xiàn)了這個(gè)監(jiān)聽(tīng)器,并且實(shí)現(xiàn)這個(gè)接口的方法如下:
????public?void?handleMessage(ID?fromID,?ID?toID,?Type?type,?String?subject,?String?messageBody)?{
????????final?ID?id?=?fromID;
????????if(type?==?Type.CHAT){
????????final?String?message?=?messageBody;
????????Display.getDefault().asyncExec(new?Runnable(){
????????????public?void?run(){
????????????????try?{
????????????????????if(id.toURI().compareTo(chaterID.toURI())?==0){
????????????????????????
????????????????????????String?s?=?chaterID.toURI().getUserInfo().toString();
????????????????????????s?+=?"?say:?"?+?message?+"\n";
????????????????????????
????????????????????????showText.append(s);
????????????????????????View.this.getSite().getWorkbenchWindow()
????????????????????????.getWorkbench().getActiveWorkbenchWindow()
????????????????????????.getActivePage().activate(
???????????????????(IViewPart)ChatPlugin.getDefault().getMessageDialogForID(chaterID));
????????????????????}
????????????????}?catch?(URISyntaxException?e)?{
????????????????????//?TODO?Auto-generated?catch?block
????????????????????e.printStackTrace();
????????????????}
????????????}
????????});}
????????
????}
????????final?ID?id?=?fromID;
????????if(type?==?Type.CHAT){
????????final?String?message?=?messageBody;
????????Display.getDefault().asyncExec(new?Runnable(){
????????????public?void?run(){
????????????????try?{
????????????????????if(id.toURI().compareTo(chaterID.toURI())?==0){
????????????????????????
????????????????????????String?s?=?chaterID.toURI().getUserInfo().toString();
????????????????????????s?+=?"?say:?"?+?message?+"\n";
????????????????????????
????????????????????????showText.append(s);
????????????????????????View.this.getSite().getWorkbenchWindow()
????????????????????????.getWorkbench().getActiveWorkbenchWindow()
????????????????????????.getActivePage().activate(
???????????????????(IViewPart)ChatPlugin.getDefault().getMessageDialogForID(chaterID));
????????????????????}
????????????????}?catch?(URISyntaxException?e)?{
????????????????????//?TODO?Auto-generated?catch?block
????????????????????e.printStackTrace();
????????????????}
????????????}
????????});}
????????
????}
可能讀者這會(huì)看上面的代碼會(huì)一頭霧水。我解釋一下:
變量chaterID是一個(gè)ID類型的,它其實(shí)是從剛才我們好友列表中,雙擊某一項(xiàng)時(shí)生成這個(gè)View對(duì)象的時(shí)候傳進(jìn)來(lái)的,讓我們看看SimpleView 中的雙擊action的代碼:
doubleClickAction?=?new?Action()?{
????????????public?void?run()?{
????????????????ISelection?selection?=?viewer.getSelection();
????????????????IRosterEntry?entry?=?(IRosterEntry)?((StructuredSelection)?selection)
????????????????????????.getFirstElement();
????????????????View?chatView?=?(View)?ChatPlugin.getDefault()
????????????????????????.getMessageDialogForID(entry.getUserID());
????????????????if?(chatView?!=?null)?{
????????????????????SampleView.this.getSite().getWorkbenchWindow()
????????????????????????????.getWorkbench().getActiveWorkbenchWindow()
????????????????????????????.getActivePage().activate(chatView);
????????????????}
????????????}
????????};
????????????public?void?run()?{
????????????????ISelection?selection?=?viewer.getSelection();
????????????????IRosterEntry?entry?=?(IRosterEntry)?((StructuredSelection)?selection)
????????????????????????.getFirstElement();
????????????????View?chatView?=?(View)?ChatPlugin.getDefault()
????????????????????????.getMessageDialogForID(entry.getUserID());
????????????????if?(chatView?!=?null)?{
????????????????????SampleView.this.getSite().getWorkbenchWindow()
????????????????????????????.getWorkbench().getActiveWorkbenchWindow()
????????????????????????????.getActivePage().activate(chatView);
????????????????}
????????????}
????????};
可以看出來(lái),當(dāng)我們雙擊某個(gè)好友的時(shí)候,就會(huì)從entry中得到他的ID,然后生成一個(gè)View,并將ID給View,所以View的chaterID就時(shí)這么來(lái)的。
接著上面的解釋:
showText變量其實(shí)是一個(gè)StyleText對(duì)象,他專門負(fù)責(zé)顯示聊天信息,而下面那一長(zhǎng)段代碼讀者大可不必理會(huì),那是為了使一個(gè)好友對(duì)應(yīng)一個(gè)View而做的一些工作,大概了解即可,也可以去看源代碼獲得更多的信息。
7.發(fā)送消息
讓我們看看View類中的一段代碼:
messageText.addKeyListener(new?KeyListener(){
????????????public?void?keyPressed(KeyEvent?e)?{
????????????????
????????????}
????????????public?void?keyReleased(KeyEvent?e)?{
????????????????if(e.character?==?'\r'){
????????????????????sendMessage(messageText.getText());
????????????????????messageText.setText("");
????????????????}
????????????}
????????????
????????});
????????????public?void?keyPressed(KeyEvent?e)?{
????????????????
????????????}
????????????public?void?keyReleased(KeyEvent?e)?{
????????????????if(e.character?==?'\r'){
????????????????????sendMessage(messageText.getText());
????????????????????messageText.setText("");
????????????????}
????????????}
????????????
????????});
不難看出這段代碼的意思:當(dāng)遇到輸入字符為回車的時(shí)候,就調(diào)用sendMessage方法:
public?void?sendMessage(String?message)?{
????????if(this.getChaterID()?==?null)?return;
????????String?s?=?"你說(shuō):";
????????s+=?message;
????????
????????ChatPlugin.getDefault().getPresenceContainer().getMessageSender()
????????????????.sendMessage(ChatPlugin.getDefault().getUserID(),chaterID,?null,?null,?message);
????????
????????showText.append(s?+?"\n");
????}
????????if(this.getChaterID()?==?null)?return;
????????String?s?=?"你說(shuō):";
????????s+=?message;
????????
????????ChatPlugin.getDefault().getPresenceContainer().getMessageSender()
????????????????.sendMessage(ChatPlugin.getDefault().getUserID(),chaterID,?null,?null,?message);
????????
????????showText.append(s?+?"\n");
????}
sendMessage方法是從ChatPlugin中獲得IPresenceContainer的messagesender去發(fā)送消息的,發(fā)送消息的函數(shù)第一個(gè)參數(shù)是發(fā)送者的ID,第二個(gè)是接收者的ID(chaterID已經(jīng)在上面講過(guò)了獲取的來(lái)源),最后一個(gè)是發(fā)送的消息,中間兩個(gè)參數(shù)一個(gè)消息類型和標(biāo)題,他們可以為空。
8.結(jié)束語(yǔ)
通過(guò)我們上面所說(shuō)的如何去登陸、獲得好友列表、接收消息和發(fā)送消息,我們已經(jīng)能夠簡(jiǎn)單地創(chuàng)建一個(gè)Google Talk的客戶端了,但是還有很多功能沒(méi)有實(shí)現(xiàn),比如添加好友、監(jiān)聽(tīng)好友狀態(tài)改變等等,這些都需要大家去增加。就講到這里,我們下次再見(jiàn)。