目前為止不管后臺(tái)寫了多少邏輯(已經(jīng)登錄了Google,取了相冊(cè)數(shù)據(jù)),我們的Gadget都還是那個(gè)看上去白白的Gadget。而要想讓它看上去有所不同,就要在main.xml這個(gè)文件中,制定我們想要的“長(zhǎng)相”(就跟征婚啟事里寫的一樣,身高1米6至1米7,體重不超過(guò)55公斤,相貌端正,賢良淑惠)。
如果你已經(jīng)下載了我提供的源碼,就可以打開(kāi)看看,對(duì)照實(shí)際效果來(lái)看代碼,應(yīng)該很好理解。我們總計(jì)在界面上放了幾樣?xùn)|西:
其中值得注意的事情有這么幾件:
一是背景圖片絕非可有可無(wú),按google的說(shuō)法,像label這種東西,如果沒(méi)有放在一張背景圖片之上的話,是顯示不出來(lái)的。
二是Gadget中界面的內(nèi)容,樣式和布局都在這一個(gè)文件中指定。
三是Gadget的界面沒(méi)有HTML那種流動(dòng)布局的效果,就是說(shuō),所有要顯示的元素,必須明明白白的指出它的位置,也就是每個(gè)元素的x和y屬性,是從該元素的父元素左上角開(kāi)始計(jì)算的坐標(biāo)。如果你先寫了一個(gè)label(拿label舉個(gè)例子,實(shí)際上用什么效果都是一樣的),再挨著它寫了一個(gè)label,兩個(gè)label你都沒(méi)有指定x和y的值,那么這個(gè)兩個(gè)label會(huì)重疊著顯示在一起。不信你可以試一試。
四是圖片的源文件位置,從代碼中可以看到指定本機(jī)上的相對(duì)目錄是可以的,那么指定一個(gè)網(wǎng)絡(luò)上的url可以么?例如http://www.sina.com.cn/images/logo.gif?如果你頭腦中還存在著HTML的印象,可能想當(dāng)然的以為可以這么做,而事實(shí)上不行,Gadget與Web沒(méi)有天然的聯(lián)系(沒(méi)記錯(cuò)的話,我已經(jīng)說(shuō)過(guò)四次了)。后面處理相冊(cè)縮略圖的時(shí)候,我們會(huì)看到怎么把網(wǎng)絡(luò)上的圖片顯示出來(lái)。
寫過(guò)圖形用戶界面程序的人一定想問(wèn),如何讓界面上的元素與代碼產(chǎn)生聯(lián)系呢?例如我們的列表,我想在代碼中對(duì)它作些修改的時(shí)候,如何取得它的引用呢?在Gadget中這一點(diǎn)還比較方便,主要有兩個(gè)途徑:一是只要你給元素賦了name屬性,例如我就給列表項(xiàng)起了一個(gè)名字叫做contentListBox,在main.xml中的這一行:
之后就可以直接在代碼中用contentListBox這個(gè)值來(lái)訪問(wèn)這個(gè)列表項(xiàng)了(而且任你在代碼中怎么找,也找不到聲明或者初始化這個(gè)變量的地方)——當(dāng)然前提是起的名字必須是唯一的。有意思吧?
第二種方式比較傳統(tǒng)也比較少用,可以通過(guò)DOM對(duì)象訪問(wèn)每個(gè)元素。
廢話不多說(shuō),來(lái)看看在代碼中給列表插入列表項(xiàng)怎么做。
列表項(xiàng)對(duì)應(yīng)著Gadget API提供的一個(gè)名為item的對(duì)象實(shí)例,但我們要用new item()這樣的語(yǔ)法來(lái)得到一個(gè)新的列表項(xiàng)并逐一設(shè)置它的屬性么?不不,有更簡(jiǎn)便也更好玩的方法,我們只要新建一個(gè)字符串:
然后調(diào)用列表contentListBox的方法來(lái)添加就可以,像這樣:
方便么?這種用法使得開(kāi)發(fā)人員不需要為一個(gè)圖形界面的組件掌握兩套語(yǔ)法(XML的和JavaScript的),非常貼心。
好,現(xiàn)在來(lái)說(shuō)另一個(gè)問(wèn)題,既然不能為一個(gè)img對(duì)象的src屬性指定一個(gè)網(wǎng)絡(luò)地址,那到底如何顯示網(wǎng)絡(luò)上的圖片呢?答案很長(zhǎng),如果你有了圖片的url(就是 http://開(kāi)頭的那種啦),首先要通過(guò)XmlHttpRequest把圖片的數(shù)據(jù)取回來(lái),然后把這部分?jǐn)?shù)據(jù)賦給src屬性。
具體點(diǎn),記得一個(gè)請(qǐng)求最重要的四部分?jǐn)?shù)據(jù)么?url:就是該圖片的url;請(qǐng)求類型:因?yàn)槭且髷?shù)據(jù),自然是“GET”;請(qǐng)求頭:對(duì)本請(qǐng)求來(lái)說(shuō)沒(méi)有;消息體:同樣沒(méi)有。
所以發(fā)請(qǐng)求的部分并不困難,待請(qǐng)求的狀態(tài)變?yōu)?/span>4,也就是說(shuō)明回傳數(shù)據(jù)已到達(dá)的時(shí)候,就可以從請(qǐng)求的responseStream這個(gè)屬性得到圖片的二進(jìn)制數(shù)據(jù)。假設(shè)在代碼中我們要顯示的圖片是<img name=”myImg”/>,記得么,使用名字可以直接訪問(wèn)這個(gè)圖片,再假設(shè)我們的請(qǐng)求對(duì)象取名為xhRequest,像下面這樣:
如此就可以了!哈哈,簡(jiǎn)單吧(我當(dāng)初倒是找了半天,讀過(guò)了YouTube Gadget的代碼才參透呢,愚笨愚笨)。
在我們剩下的唯一一個(gè)重要函數(shù)Main的fetchAlbumThumbnail()中,就是使用這種方法來(lái)取得相冊(cè)縮略圖的圖片并顯示在Gadget的界面中的。
這個(gè)函數(shù)我就不逐一分解了,相信你一定看得懂。
大的方向上說(shuō),從Picasa服務(wù)器上取數(shù)據(jù),有兩種方式,一種是使用Google已經(jīng)開(kāi)放的各種語(yǔ)言的API,可以在頁(yè)面http://code.google.com/apis/picasaweb/developers_guide_protocol.html找到很多相關(guān)的信息。另一種方式便是使用最樸素的網(wǎng)絡(luò)請(qǐng)求方式來(lái)自己構(gòu)造請(qǐng)求并解析回傳的數(shù)據(jù)。
由于Picasa只提供了Java,.NET,Python和PHP的接口,而Gadget目前只能使用JavaScript,因此我們只能使用樸素方式。
繼續(xù)第三節(jié)的路子,仍然使用XmlHttpRequest向Picasa服務(wù)發(fā)起請(qǐng)求,也要處理好四部分信息。
請(qǐng)求發(fā)向哪個(gè)URL:為了獲取Picasa的相冊(cè)信息,要向http://picasaweb.google.com/data/feed/api/user/default發(fā)請(qǐng)求,這個(gè)URL其實(shí)可以有很多變化的地方。例如user/default這個(gè)地方是請(qǐng)求所附token所屬的用戶相冊(cè)信息,這里當(dāng)然可以明確的指定用戶名。”api”可以換成”base”,這個(gè)將影響回傳數(shù)據(jù)的格式,但Goolge推薦使用api而不是base。
請(qǐng)求的類型:我們是要索取數(shù)據(jù),因此這是一個(gè)查詢的動(dòng)作,應(yīng)該使用GET。
請(qǐng)求頭:只需要把token放進(jìn)去就好。這樣來(lái)放:
消息體:對(duì)于我們查詢相冊(cè)的請(qǐng)求,不需要任何的消息體。
具體的代碼都在Main.prototype.fetchAlbumsInfo()函數(shù)中,就像這樣:
最后兩個(gè)函數(shù)是下一步要做的工作:解析回傳的相冊(cè)數(shù)據(jù),并下載每個(gè)相冊(cè)的縮略圖。
要想解析回傳數(shù)據(jù),首先得知道回傳的數(shù)據(jù)是什么。你可以把這些數(shù)據(jù)打印出來(lái)看看,應(yīng)該是類似下面的樣子:
怎么,看著有點(diǎn)眼熟?沒(méi)錯(cuò),這個(gè)回傳數(shù)據(jù)所使用的格式正是標(biāo)準(zhǔn)的Atom Feed(更多的描述可以參考W3C的標(biāo)準(zhǔn)和下面的鏈接:http://code.google.com/intl/zh-CN/apis/picasaweb/developers_guide_protocol.html)。
可以根據(jù)Atom Feed的格式來(lái)編寫我們解析回傳數(shù)據(jù)的函數(shù)parseAlbumFeed(),這個(gè)函數(shù)的作用是從回傳的xml數(shù)據(jù)中找出我們關(guān)心的幾樣?xùn)|西:該用戶目前擁有的所有的相冊(cè)信息,包括每個(gè)相冊(cè)的標(biāo)題,描述,訪問(wèn)權(quán)限以及縮略圖的地址。找出這些信息以后,將會(huì)拼成一個(gè)包含相冊(cè)(Album)的數(shù)組作為函數(shù)返回值。
具體代碼如下:
這個(gè)函數(shù)中用到了一些我們還沒(méi)有新建的類,相冊(cè)(Album)以及縮略圖(Thumbnail)。這些類的聲明可以放在一個(gè)新的名為album.js的文件中,并在我們整個(gè)Gadget的main.xml文件中指名要導(dǎo)入它。因此main.xml的最后幾行應(yīng)該看上去是這個(gè)樣子:
而album.js的內(nèi)容大體如下:
最后還要在main.js里面添加一個(gè)函數(shù)createDomDocument(),用來(lái)提供一個(gè)DOM對(duì)象供我們解析XML用。代碼如下:
下一節(jié)來(lái)說(shuō)說(shuō)怎么取得相冊(cè)的縮略圖并顯示在Gadget的界面中。
向Google的服務(wù)器發(fā)起登錄請(qǐng)求之后,得到了免死金牌token,以后就可以拿著這個(gè)token去犯罪,不是,去Google的其它服務(wù)取數(shù)據(jù),但是在此之前應(yīng)該第一,從響應(yīng)的消息中把token找出來(lái);第二,這個(gè)token應(yīng)該想辦法保存起來(lái),以備以后使用。
上一節(jié)已經(jīng)把響應(yīng)的內(nèi)容打印了出來(lái),它的格式也很簡(jiǎn)單,因此用下面的代碼很容易就可以把響應(yīng)的內(nèi)容轉(zhuǎn)成方便我們使用的形式,即一個(gè)map的形式,通過(guò)鍵值對(duì)來(lái)存儲(chǔ):
在我們的相應(yīng)回調(diào)函數(shù)里,就可以調(diào)用這個(gè)函數(shù)處理一下響應(yīng),從結(jié)果中取鍵為”Auth”這一項(xiàng)的值,并保存在Gadget Host為我們準(zhǔn)備好的一個(gè)負(fù)責(zé)持久化的對(duì)象options中。找到上一節(jié)Main.prototype.login的代碼,把響應(yīng)的回調(diào)函數(shù)改成下面的樣子:
最后加的一行main.onLoginSuccess()就是我們下一步動(dòng)作的起點(diǎn),在這里應(yīng)該開(kāi)始去取用戶mymail2009.test@gmail.com所擁有的相冊(cè)信息了,我們先聲明一個(gè)空函數(shù)放在那里。
繼續(xù)之前多扯兩句options這個(gè)對(duì)象,這是Gadget Host提供的持久化對(duì)象,你可以從代碼中看到它還有對(duì)存儲(chǔ)的內(nèi)容進(jìn)行加密的功能,Google的文檔中提到這個(gè)options對(duì)象在后臺(tái)實(shí)際上是把內(nèi)容保存在一個(gè)XML文件中,當(dāng)然該文件的位置是不會(huì)告訴你的啦,哈哈。
下一節(jié)將向Picasa服務(wù)發(fā)起請(qǐng)求!
Google帳戶最早用來(lái)申請(qǐng)巨大的Gmail郵箱(如今看來(lái),一般個(gè)大吧),隨著后來(lái)的Blogger,Picasa,Docs等各種服務(wù)上線,也就順路繼承了過(guò)來(lái)。現(xiàn)在使用一個(gè)Google帳戶,就可以同時(shí)使用這些服務(wù)。
既然我們打算寫一個(gè)從Picasa取相冊(cè)數(shù)據(jù)的Gadget,就免不了要先了解一些和Goolge帳戶有關(guān)的知識(shí)。因?yàn)?/span>Picasa的數(shù)據(jù)也是受保護(hù)的,并非誰(shuí)要看都可以(公開(kāi)的相冊(cè)除外哦,那都是炫耀冊(cè),巴不得全天下人都看見(jiàn)呢),我們的程序也不例外,要想取到相冊(cè)的數(shù)據(jù),程序必須向Google的服務(wù)器證明自己得到了相應(yīng)用戶的授權(quán)。
一個(gè)人類用戶當(dāng)然可以這樣做:打開(kāi)Picasa的首頁(yè),發(fā)現(xiàn)要求登錄,于是輸入自己的用戶名密碼,成功后就查看自己的相冊(cè)。我們的程序可干不了,它不會(huì)打開(kāi)瀏覽器,好吧,這個(gè)它會(huì),但打開(kāi)以后它找不著用戶名的輸入框在哪,即便找到了,也不知該往里面填什么,即便填對(duì)了,也不知要看什么,即便看到了,也看不懂,即便看懂了也學(xué)不會(huì)……(讀者:你貧不貧?)
所以一切的一切都還要咱們自己來(lái)寫,當(dāng)然少不了Google的幫忙。
為了方便應(yīng)用程序的登錄,Google在自己的服務(wù)器上開(kāi)放了被稱之為“Google Account Authentication”的服務(wù),我們只用到其中一種方式:ClientLogin。使用這種方式訪問(wèn)Google的服務(wù)大致是下面的流程:
很容易看出來(lái),這基本上是一個(gè)兩步驟的工作:首先使用一個(gè)Google帳戶訪問(wèn)Google Account Authentication 服務(wù),并得到一個(gè)可以合法訪問(wèn)服務(wù)數(shù)據(jù)的token(Google把它叫做得到一個(gè)“授權(quán)”,不過(guò)習(xí)慣上還是叫token吧,就是令牌,拿了以后皇帝不能砍你頭的那種,此過(guò)程也叫做申請(qǐng)一個(gè)token);使用上一步得到的token去訪問(wèn)具體的服務(wù)并取得數(shù)據(jù)(我們的例子中就是訪問(wèn)Picasa服務(wù))。
有一些東西從圖上看不出,我來(lái)說(shuō)一說(shuō)。一是程序訪問(wèn)Gmail的時(shí)候使用的不是這種方式(畢竟Gmail太早啦,那時(shí)連Google自己都沒(méi)有考慮清楚吧),但其他大部分Goolge服務(wù),包括Calendar,Docs,Picasa,Blogger,Contacts,Google Apps等等,都是上面這個(gè)流程。二是并非申請(qǐng)了一個(gè)token以后,就可以訪問(wèn)Google所有的服務(wù),實(shí)際上需要為每個(gè)服務(wù)申請(qǐng)不同的token。
具體到代碼中,我們使用XmlHttpRequest對(duì)象來(lái)發(fā)送請(qǐng)求并且接受回傳的數(shù)據(jù)。
XmlHttpRequest是Gadget Host提供的一個(gè)類型(注意我沒(méi)有說(shuō)對(duì)象,因此要用的時(shí)候你還得自己初始化,也就是new一下,哈哈),其行為與W3C所指定的標(biāo)準(zhǔn)XmlHttpRequest相同。再一次的,不要聯(lián)想到瀏覽器,你不能假設(shè)這個(gè)XmlHttpRequest與IE或者FireFox提供的XmlHttpRequest有任何聯(lián)系,更不能依賴這樣的假設(shè)來(lái)編寫程序。
好,廢話少說(shuō),還用上一節(jié)新建的“白Gadget“(笑),在main.js文件里添加這樣一個(gè)函數(shù):
調(diào)用這個(gè)函數(shù)就可以得到一個(gè)XmlHttpRequest的對(duì)象啦。
然后為我們的Gadget添加一個(gè)主類,并把需要的對(duì)象引用也聲明好,這些都寫在main.js文件中,像這樣:
我們就要在Main.login()函數(shù)中寫我們?nèi)?/span>token的邏輯。
詳細(xì)說(shuō)說(shuō)申請(qǐng)token的過(guò)程。請(qǐng)求是通過(guò)XmlHttpRequest對(duì)象發(fā)起的,而對(duì)一個(gè)請(qǐng)求來(lái)說(shuō),最重要的信息有四個(gè):請(qǐng)求的URL,請(qǐng)求的類型,請(qǐng)求頭和消息體。
URL是說(shuō)你的請(qǐng)求要發(fā)往哪里,既然我們要使用Google的服務(wù),那當(dāng)然要往Google那里發(fā)了,具體應(yīng)該為:
https://www.google.com/accounts/ClientLogin
如果你沒(méi)有看出這是一個(gè)安全的https請(qǐng)求,那我提醒一下(如果你看出來(lái)了,我就不提醒了,笑)。
請(qǐng)求的類型是指你要Google的服務(wù)器替你做什么事情,是返回你要查詢的數(shù)據(jù)?還是為你更新已有的數(shù)據(jù),抑或僅僅是提交一些數(shù)據(jù),還是要服務(wù)器幫你刪除一些數(shù)據(jù)?
Google的服務(wù)器通過(guò)你提交請(qǐng)求的類型來(lái)做相應(yīng)的操作,每一種操作的類型對(duì)應(yīng)如下:
看著眼熟么?沒(méi)錯(cuò),正是輕量級(jí)的Web Service接口REST!
我們做登錄顯然是一個(gè)提交的動(dòng)作, 要把我們的用戶名和密碼告訴Google,因此我們的請(qǐng)求類型是POST。
對(duì)登錄來(lái)說(shuō),請(qǐng)求頭沒(méi)有特殊要求,只需要請(qǐng)求頭Content-Type
,
其值為application/x-www-form-urlencoded
所需的用戶名,密碼等信息被統(tǒng)一稱為“屬性”,屬性的值將放在消息體中發(fā)送。因此你的消息體看起來(lái)是下面這個(gè)樣子的一個(gè)字符串:
Email=mymail2009.test%40gmail.com&Passwd=mymail2009&service=lh2&source=gd-picasa-gadget-1.0.0.0&accountType=HOSTED_OR_GOOGLE
注意其中紅色的部分,用戶名和密碼的位置你當(dāng)然很容易找到,”service=lh2”這一項(xiàng)就指明了你要為訪問(wèn)什么服務(wù)申請(qǐng)token,lh2是指Picasa,如果訪問(wèn)Google Docs則要填writely,詳細(xì)的列表可以看這一節(jié)最后的附錄。
好,把登錄的代碼整個(gè)貼出來(lái),你應(yīng)該很容易找到以上四部分對(duì)應(yīng)的地方。
在請(qǐng)求的回調(diào)函數(shù)中,目前只是先簡(jiǎn)單的打印了相應(yīng)的文本內(nèi)容,實(shí)際上應(yīng)該在這里做更多的事,詳情咱們下節(jié)再聊。如果你看到類似下面這樣的輸出內(nèi)容,說(shuō)明登錄的請(qǐng)求成功了。如果沒(méi)有成功,很可能是因?yàn)槲乙呀?jīng)換掉了用戶名和密碼,用你自己的Google帳戶試試看。
應(yīng)該看到的內(nèi)容:
SID=
DQAAAHYAAADYQ4hToTAEYRu0uEXP9yXZ1uc_W3-kBtZFpug78XQDGiykOb-Sv2qdXtdUOL-
npRJm9SSq-AEvSBodrcuy3UwgFM8SX_z6fXzpGaJzHzQx5YTzR0AJHCEkFh
4yOoBFs0iCE2LI0LWQs6_2BFyIuLLMwRA8m3vfuVzNE3CHjrUHZA
LSID=
DQAAAHgAAAClSiMWRfKAonW8zIytZ7NEizJNMQZojiNqsDxm3elei36MV
7GzM72bMiqdQawt8Fd1Dpp68p5bs1XYOXUPmDunUsZM1BZsAiXbIEouAJz1XjlysUQG-0p9969zYCvUm2tqWkA1BFVU2UqvjMAaBSgj10VkZzvcAbZB8nQf_mwRyg
Auth=
DQAAAHcAAAClSiMWRfKAonW8zIytZ7NEizJNMQZojiNqsDxm3elei36
MV7GzM72bMiqdQawt8FcmxySIt75kfLxcis5BZnNCsyVuCwKM-DtNZcToUtm9IWoJyvNbUD9UTFYZPdBu1OyXsfY_QJHZfZdAT2QC
cExSIYKMvLfhhit9RPz4Gk2xlQ/n
Auth那一項(xiàng)后面的值就是token啦,可以不被砍頭了。
附錄:已知的Google服務(wù)及服務(wù)名
Calendar Data API |
cl |
Google Base Data API |
gbase |
Blogger Data API |
blogger |
Contacts Data API |
cp |
Documents List Data API |
writely |
Picasa Web Albums Data API |
lh2 |
Google Apps Provisioning API |
apps |
Spreadsheets Data API |
wise |
YouTube Data API |
youtube |
用到的工具有兩個(gè),一個(gè)是隨Google Desktop SDK附帶的Gadget Designer,用來(lái)編寫并有限的預(yù)覽界面,還可以調(diào)試JavaScript(這個(gè)就更有限了);一個(gè)是Google Desktop,用來(lái)測(cè)試寫好的Gadget。下面要寫的例子是我在為某研究院某個(gè)項(xiàng)目策劃階段作POC時(shí)所寫的一個(gè)小例子,可以顯示一個(gè)Google用戶的Picasa相冊(cè)中的Album名稱和縮略圖。雖然很小,但包含了Google賬戶的自動(dòng)登錄,顯示網(wǎng)絡(luò)圖片,XmlHttpRequest的使用等很多實(shí)用技巧。整個(gè)完成之后是這個(gè)樣子:
請(qǐng)跟我一起來(lái)。現(xiàn)在打開(kāi)Gadget Designer,選擇File->New Gadget,輸入了名稱“Picasa”之后,就可以看到一個(gè)完整Gadget的雛形了。你可以找到這個(gè)項(xiàng)目所在的文件夾,雙擊其中的gadget.gmanifest,此時(shí)如果你已經(jīng)安裝了Google Desktop,就可以看到Desktop自動(dòng)啟動(dòng),并把這個(gè)很“白”的Gadget(別笑,除了一張白色背景圖片以外,確實(shí)什么也沒(méi)有)顯示在Sidebar中。如圖:
到項(xiàng)目文件夾里可以看到一個(gè)main.xml文件和一個(gè)main.js文件。我們的界面就是在main.xml文件里指定的,打開(kāi)它,可以看見(jiàn)它指定了一張GadgetDesigner幫我們生成的白色png圖片作背景,還指定了我們要導(dǎo)入哪些個(gè).js文件。我們來(lái)小改兩個(gè)地方:
一是把view的height改成250,二是給img元素添加一個(gè)屬性name并給一個(gè)值,就像這樣:
然后雙擊gadget.gmanifest,看看更改效果:
乍一看貌似沒(méi)什么改變,但是注意看我用黑色線圈出來(lái)的那一條橫杠,那是我們的Gadget的下邊沿,說(shuō)明它的高度還是變化了,但是白色的背景沒(méi)有變,因?yàn)槲覀儧](méi)有改變背景圖片的大小。現(xiàn)在通過(guò).js文件中代碼的方式來(lái)改變背景圖片的高度,可以看出些有意思的東西。
打開(kāi)main.js文件,你應(yīng)該會(huì)看到一個(gè)view_onOpen()函數(shù),這就是Gadget啟動(dòng)時(shí)會(huì)自動(dòng)調(diào)用的第一個(gè)函數(shù)(好吧,并不嚴(yán)格,但是在調(diào)用的順序上,它的確是相當(dāng)靠前的),我們就在這個(gè)函數(shù)內(nèi)部添加下面這一句:
再雙擊gadget.gmanifest運(yùn)行看看,白色背景也變高了吧。
我知道你一定會(huì)問(wèn),代碼里的bgImage是什么東西?怎么沒(méi)見(jiàn)在任何地方聲明這個(gè)變量,也沒(méi)見(jiàn)任何地方作初始化呢?回想我們剛才在main.xml文件里做了什么?我們給背景圖片取了一個(gè)名字,叫bgImage,而且別懷疑,你在代碼里訪問(wèn)的這個(gè)bgImage,正是那張圖片!背后的工作就是Gadget Host通過(guò)JavaScript引擎為我們做的,凡是在.xml文件里放置的東西(無(wú)論什么,圖片也好,按鈕也好,一個(gè)抽象的div也好),只要你給了一個(gè)name屬性,在JavaScript代碼中就可以直接使用這個(gè)名字來(lái)訪問(wèn)該對(duì)象(前提是你給的名字得是獨(dú)一無(wú)二的),這與瀏覽器中隨時(shí)可以訪問(wèn)document對(duì)象而不用做任何聲明一樣,那是瀏覽器這個(gè)運(yùn)行環(huán)境提供的對(duì)象,隨時(shí)可用。
另一個(gè)值得注意的地方是在.xml文件里,屬性的值都必須加上引號(hào),像height=”250”(因?yàn)槟抢锸褂玫氖菢?biāo)準(zhǔn)的xml語(yǔ)法),而在JavaScript代碼中,就要根據(jù)屬性具體的類型來(lái)決定,像高度這種整數(shù)型的值,就不用加。
你可能還會(huì)問(wèn),那么bgImage這個(gè)對(duì)象,是什么類型的,它有些什么屬性和方法可供我使用呢?它是一個(gè)img類型的對(duì)象,參考http://code.google.com/intl/zh-CN/apis/desktop/docs/gadget_apiref.html這個(gè)鏈接,這也是Google Desktop Gadget的API參考頁(yè)面,列出了Gadget Host提供的各種對(duì)象屬性和方法的說(shuō)明(雖然事實(shí)驗(yàn)證,Google自己列的這些都不全面,后話)。
最后叮囑一句:盡管main.xml文件里的東西(什么img啊,以后還會(huì)加進(jìn)div啊,checkbox之類的東西)看起來(lái)多么的像HTML,Gadget都和Web沒(méi)有天然的聯(lián)系。Google自己發(fā)布了一些Gadget,例如Gmail和Google Docs,外觀與這兩個(gè)服務(wù)的網(wǎng)頁(yè)非常像,再加上Gadget也主要使用JavaScript開(kāi)發(fā)(也少不了Universal Gadget跟著摻合),間接導(dǎo)致了總有人把Gadget顯示的地方考慮成一個(gè)小的瀏覽器窗口,而想把Web的一些東西簡(jiǎn)單的放在這里,到底行不行呢?李寧說(shuō):一切皆有可能。阿迪說(shuō):沒(méi)有不可能。匹克說(shuō):我能,無(wú)限可能。我要說(shuō):可能,但很難(笑)。
所以在編寫Gadget的時(shí)候,最好的方法是把它當(dāng)成純粹的桌面程序,忘掉Web的那一套。
這一節(jié)給大家入個(gè)門,下一節(jié)開(kāi)始說(shuō)說(shuō)在Gadget中怎么做Google帳戶的登錄,還會(huì)很羅嗦的,請(qǐng)見(jiàn)諒(笑)。
下面是一個(gè)Gadget項(xiàng)目在Google Desktop Disigner里面的結(jié)構(gòu)截圖。
資源這東西好理解,無(wú)非是程序要用到的各種圖片啦,字符串啦等等。讀者:字符串?什么意思?答:把程序會(huì)用到的一系列字符串統(tǒng)一存放,想引用的時(shí)候使用一個(gè)常量名字就可以,而不必在需要這些字符串的地方每次都重寫一遍,和Java中的property文件作用類似。
其余的兩部分會(huì)分節(jié)來(lái)詳細(xì)講解。
當(dāng)然說(shuō)只有三部分,是指我們大多只關(guān)心這么多,實(shí)際上還有第四部分,一個(gè)Gadget Settings文件,其中大多是關(guān)于這個(gè)Gadget的元信息,什么作者啊,創(chuàng)建日期啊,uuid啊,戶口所在地啊,最高學(xué)歷啊,婚姻狀況啊,哦,我給說(shuō)成簡(jiǎn)歷了(笑)。
前面也說(shuō)到過(guò),一個(gè)Gadget其實(shí)就是一個(gè)桌面應(yīng)用程序(再一次的,不管寫起來(lái)某些語(yǔ)法多么得像HTML,Gadget與Web都沒(méi)有天然的聯(lián)系),只不過(guò)這個(gè)程序在Gadget Host的管理之下,行話叫“托管”。Windows下沒(méi)有單獨(dú)的Gadget Host,它被合并在Google Desktop里面(算是另一種捆綁吧)。而Linux下的確有干干凈凈的Gadget Host,且有源碼下載,我們所有對(duì)Gadget的理解也都源于這個(gè)版本和相關(guān)的文檔。
那么在Gadget Host看來(lái),一個(gè)Gadget是什么東西呢?
以我寫的一個(gè)小Picasa Gadget為例,在Picasa Gadget初次加載之前,它是一個(gè).gg的壓縮包(其實(shí)就是一個(gè)標(biāo)準(zhǔn)的zip包,被改了后綴名而已),Gadget Host會(huì)從中讀取需要的文件,然后做相應(yīng)的解釋。
Gadget Host可以看成只有兩部分組成:一個(gè)UI的渲染器和一個(gè)JavaScript引擎。
說(shuō)UI渲染器之前就不得不回頭重提剛才說(shuō)到的一個(gè)Gadget包括了一系列.xml文件這件事。實(shí)際上這些.xml文件就是用來(lái)指定你想寫的Gadget的界面的,就是說(shuō),你的Gadget跑起來(lái)以后長(zhǎng)成什么樣子,是由這些個(gè).xml文件來(lái)決定的(當(dāng)然,嚴(yán)格說(shuō)來(lái)可以使用JavaScript在運(yùn)行時(shí)改變一些內(nèi)容,但請(qǐng)不要抬杠,笑)。
這些.xml文件中最主要的是main.xml這個(gè)文件,你的Gadget窗口有多大,在什么位置有幾個(gè)按鈕,列表有沒(méi)有滾動(dòng)條,背景是什么顏色等等,都在這里指定。還包括這些東西上的事件監(jiān)聽(tīng)函數(shù)也一并在這里聲明(不知為何,讓我莫名的想起微軟的MFC,當(dāng)然,嚴(yán)格說(shuō)來(lái)可以使用JavaScript在運(yùn)行時(shí)動(dòng)態(tài)改變這些內(nèi)容,但請(qǐng)不要再次抬杠,笑)。
UI渲染器干什么呢?就是來(lái)把這個(gè).xml所要求的界面轉(zhuǎn)換成具體的系統(tǒng)調(diào)用,讓操作系統(tǒng)來(lái)完成繪圖(好吧好吧,你喜歡嚴(yán)格,那我告訴你,Linux版本下首先被轉(zhuǎn)換為Qt的C++類,由Qt來(lái)發(fā)起對(duì)系統(tǒng)繪圖的調(diào)用)。
既然Gadget的程序邏輯都使用JavaScript來(lái)編寫,理所應(yīng)當(dāng)?shù)模珿adget Host必然要包含一個(gè)JavaScript解釋器來(lái)解釋這些代碼,這個(gè)解釋器也被叫做JavaScript引擎。Gadget Host里確實(shí)有這么個(gè)東西,叫做Spider Monkey,它恰好也是FireFox所使用的JavaScript引擎。廣義上說(shuō),一個(gè)引擎的作用主要是解釋它遇到的一切JavaScript代碼,如果代碼使用到核心JavaScript的功能和對(duì)象,它便直接提供;如果代碼使用到了一些依賴于底層的對(duì)象(例如Gadget Host就提供了很多專有的JavaScript對(duì)象和方法供使用,這些都是核心JavaScript之外的東西),則引擎還要負(fù)責(zé)轉(zhuǎn)發(fā)這樣的請(qǐng)求(你可以說(shuō),這實(shí)際上是適配器做的事,我這樣簡(jiǎn)化有助于理解,請(qǐng)不要一再抬杠,笑)。
也可以這樣從邏輯上看Gadget的組成:即一個(gè)Gadget就是一組圖形界面,加這些界面上每個(gè)控件(按鈕啊,列表啊,輸入框等等)的事件監(jiān)聽(tīng)函數(shù),這種界面描述與事件邏輯分離的程序模型,和微軟的XAML+C#簡(jiǎn)直如出一轍。因此一個(gè)Gadget的開(kāi)發(fā)實(shí)際上也就可以分為這兩大步驟:先寫界面的XML文件,再寫邏輯部分的JavaScript。下面一節(jié)就用一個(gè)小例子來(lái)看看具體如何做。別嫌我說(shuō)得太詳細(xì)哦。