網店的Logo。那大腿不是別人的,正是韓國歌星寶兒……
她跟我說上面這張圖最大的問題就在于太有夜店風格,與她的店不符。不過用著用著,她自己倒也喜歡上了。
這個是剛出爐的5月新款的預告,照片里的人可全是她……
Jasper's Java Jacal
嘉士伯的Java小屋
|
|
BlogJava |
首頁 |
發新隨筆 |
發新文章 |
聯系 |
聚合![]() |
隨筆:51 文章:2 評論:717 引用:0 |
前文說道開發一個Gadget可以分為兩個步驟:先寫界面的XML文件,再寫邏輯部分的JavaScript。我們就遵循這個步驟來寫一個再簡單也不過的Gadget。 用到的工具有兩個,一個是隨Google Desktop SDK附帶的Gadget Designer,用來編寫并有限的預覽界面,還可以調試JavaScript(這個就更有限了);一個是Google Desktop,用來測試寫好的Gadget。下面要寫的例子是我在為某研究院某個項目策劃階段作POC時所寫的一個小例子,可以顯示一個Google用戶的Picasa相冊中的Album名稱和縮略圖。雖然很小,但包含了Google賬戶的自動登錄,顯示網絡圖片,XmlHttpRequest的使用等很多實用技巧。整個完成之后是這個樣子: 請跟我一起來?,F在打開Gadget Designer,選擇File->New Gadget,輸入了名稱“Picasa”之后,就可以看到一個完整Gadget的雛形了。你可以找到這個項目所在的文件夾,雙擊其中的gadget.gmanifest,此時如果你已經安裝了Google Desktop,就可以看到Desktop自動啟動,并把這個很“白”的Gadget(別笑,除了一張白色背景圖片以外,確實什么也沒有)顯示在Sidebar中。如圖: 到項目文件夾里可以看到一個main.xml文件和一個main.js文件。我們的界面就是在main.xml文件里指定的,打開它,可以看見它指定了一張GadgetDesigner幫我們生成的白色png圖片作背景,還指定了我們要導入哪些個.js文件。我們來小改兩個地方: <view height="150" width="250" onopen="view_onOpen()">
<img src="stock_images\background.png" /> <script src="main.js" /> </view> 一是把view的height改成250,二是給img元素添加一個屬性name并給一個值,就像這樣: <img name=”bgImage” src="stock_images\background.png" />
然后雙擊gadget.gmanifest,看看更改效果: 乍一看貌似沒什么改變,但是注意看我用黑色線圈出來的那一條橫杠,那是我們的Gadget的下邊沿,說明它的高度還是變化了,但是白色的背景沒有變,因為我們沒有改變背景圖片的大小?,F在通過.js文件中代碼的方式來改變背景圖片的高度,可以看出些有意思的東西。 打開main.js文件,你應該會看到一個view_onOpen()函數,這就是Gadget啟動時會自動調用的第一個函數(好吧,并不嚴格,但是在調用的順序上,它的確是相當靠前的),我們就在這個函數內部添加下面這一句: bgImage.height=250;
再雙擊gadget.gmanifest運行看看,白色背景也變高了吧。 我知道你一定會問,代碼里的bgImage是什么東西?怎么沒見在任何地方聲明這個變量,也沒見任何地方作初始化呢?回想我們剛才在main.xml文件里做了什么?我們給背景圖片取了一個名字,叫bgImage,而且別懷疑,你在代碼里訪問的這個bgImage,正是那張圖片!背后的工作就是Gadget Host通過JavaScript引擎為我們做的,凡是在.xml文件里放置的東西(無論什么,圖片也好,按鈕也好,一個抽象的div也好),只要你給了一個name屬性,在JavaScript代碼中就可以直接使用這個名字來訪問該對象(前提是你給的名字得是獨一無二的),這與瀏覽器中隨時可以訪問document對象而不用做任何聲明一樣,那是瀏覽器這個運行環境提供的對象,隨時可用。 另一個值得注意的地方是在.xml文件里,屬性的值都必須加上引號,像height=”250”(因為那里使用的是標準的xml語法),而在JavaScript代碼中,就要根據屬性具體的類型來決定,像高度這種整數型的值,就不用加。 你可能還會問,那么bgImage這個對象,是什么類型的,它有些什么屬性和方法可供我使用呢?它是一個img類型的對象,參考http://code.google.com/intl/zh-CN/apis/desktop/docs/gadget_apiref.html這個鏈接,這也是Google Desktop Gadget的API參考頁面,列出了Gadget Host提供的各種對象屬性和方法的說明(雖然事實驗證,Google自己列的這些都不全面,后話)。 最后叮囑一句:盡管main.xml文件里的東西(什么img啊,以后還會加進div啊,checkbox之類的東西)看起來多么的像HTML,Gadget都和Web沒有天然的聯系。Google自己發布了一些Gadget,例如Gmail和Google Docs,外觀與這兩個服務的網頁非常像,再加上Gadget也主要使用JavaScript開發(也少不了Universal Gadget跟著摻合),間接導致了總有人把Gadget顯示的地方考慮成一個小的瀏覽器窗口,而想把Web的一些東西簡單的放在這里,到底行不行呢?李寧說:一切皆有可能。阿迪說:沒有不可能。匹克說:我能,無限可能。我要說:可能,但很難(笑)。 所以在編寫Gadget的時候,最好的方法是把它當成純粹的桌面程序,忘掉Web的那一套。 這一節給大家入個門,下一節開始說說在Gadget中怎么做Google帳戶的登錄,還會很羅嗦的,請見諒(笑)。
在Gadget開發人員看來——我當然是指你我這樣的IT民工,來開發一個Gadget的人,而不是Google大樓里成天琢磨怎么和微軟對著干的那幫子人——一個Gadget由三大部分組成:描述UI的一系列.xml文件;存放程序邏輯的.js文件以及資源。 下面是一個Gadget項目在Google Desktop Disigner里面的結構截圖。
資源這東西好理解,無非是程序要用到的各種圖片啦,字符串啦等等。讀者:字符串?什么意思?答:把程序會用到的一系列字符串統一存放,想引用的時候使用一個常量名字就可以,而不必在需要這些字符串的地方每次都重寫一遍,和Java中的property文件作用類似。 其余的兩部分會分節來詳細講解。 當然說只有三部分,是指我們大多只關心這么多,實際上還有第四部分,一個Gadget Settings文件,其中大多是關于這個Gadget的元信息,什么作者啊,創建日期啊,uuid啊,戶口所在地啊,最高學歷啊,婚姻狀況啊,哦,我給說成簡歷了(笑)。 前面也說到過,一個Gadget其實就是一個桌面應用程序(再一次的,不管寫起來某些語法多么得像HTML,Gadget與Web都沒有天然的聯系),只不過這個程序在Gadget Host的管理之下,行話叫“托管”。Windows下沒有單獨的Gadget Host,它被合并在Google Desktop里面(算是另一種捆綁吧)。而Linux下的確有干干凈凈的Gadget Host,且有源碼下載,我們所有對Gadget的理解也都源于這個版本和相關的文檔。 那么在Gadget Host看來,一個Gadget是什么東西呢? 以我寫的一個小Picasa Gadget為例,在Picasa Gadget初次加載之前,它是一個.gg的壓縮包(其實就是一個標準的zip包,被改了后綴名而已),Gadget Host會從中讀取需要的文件,然后做相應的解釋。 Gadget Host可以看成只有兩部分組成:一個UI的渲染器和一個JavaScript引擎。 說UI渲染器之前就不得不回頭重提剛才說到的一個Gadget包括了一系列.xml文件這件事。實際上這些.xml文件就是用來指定你想寫的Gadget的界面的,就是說,你的Gadget跑起來以后長成什么樣子,是由這些個.xml文件來決定的(當然,嚴格說來可以使用JavaScript在運行時改變一些內容,但請不要抬杠,笑)。 這些.xml文件中最主要的是main.xml這個文件,你的Gadget窗口有多大,在什么位置有幾個按鈕,列表有沒有滾動條,背景是什么顏色等等,都在這里指定。還包括這些東西上的事件監聽函數也一并在這里聲明(不知為何,讓我莫名的想起微軟的MFC,當然,嚴格說來可以使用JavaScript在運行時動態改變這些內容,但請不要再次抬杠,笑)。 UI渲染器干什么呢?就是來把這個.xml所要求的界面轉換成具體的系統調用,讓操作系統來完成繪圖(好吧好吧,你喜歡嚴格,那我告訴你,Linux版本下首先被轉換為Qt的C++類,由Qt來發起對系統繪圖的調用)。 既然Gadget的程序邏輯都使用JavaScript來編寫,理所應當的,Gadget Host必然要包含一個JavaScript解釋器來解釋這些代碼,這個解釋器也被叫做JavaScript引擎。Gadget Host里確實有這么個東西,叫做Spider Monkey,它恰好也是FireFox所使用的JavaScript引擎。廣義上說,一個引擎的作用主要是解釋它遇到的一切JavaScript代碼,如果代碼使用到核心JavaScript的功能和對象,它便直接提供;如果代碼使用到了一些依賴于底層的對象(例如Gadget Host就提供了很多專有的JavaScript對象和方法供使用,這些都是核心JavaScript之外的東西),則引擎還要負責轉發這樣的請求(你可以說,這實際上是適配器做的事,我這樣簡化有助于理解,請不要一再抬杠,笑)。 也可以這樣從邏輯上看Gadget的組成:即一個Gadget就是一組圖形界面,加這些界面上每個控件(按鈕啊,列表啊,輸入框等等)的事件監聽函數,這種界面描述與事件邏輯分離的程序模型,和微軟的XAML+C#簡直如出一轍。因此一個Gadget的開發實際上也就可以分為這兩大步驟:先寫界面的XML文件,再寫邏輯部分的JavaScript。下面一節就用一個小例子來看看具體如何做。別嫌我說得太詳細哦。 去年這一年被研究生院和所里揪著干了不少自己并不擅長的事,其中就包括為各種大小活動設計PPT,邀請函,節目表之類的東西。年底了,拿出來看看也能理一理自己從無到有,從門外漢到菜鳥的成長過程。共賞,共析哈。 最早的一張,元旦晚會時為模特隊做的。本來他們自己做了一套共計3張的PPT,趕巧我這張已經做好了,便讓他們自己選,結果還是選了我的,呵呵。
同一場晚會為舞蹈隊做的,沒太多東西,只是字體和配色斟酌了一陣子。
青年博客大賽決賽頒獎晚會的主題PPT,剛提交第一版便遭到老師表揚,結果一點修改都沒有做便獲通過。
同一場晚會的領導致辭圖,風格還挺一致的吧?(笑)
仍然是博客大賽決賽頒獎晚會,那一次晚會因為外請節目比較多,整體水平著實不低呢。
計算所青年歌手大賽的節目單,其實參考了很有名氣的設計,所以才能做成這個樣子,不敢專美,特此聲明。
計算所青歌賽的主題PPT,多虧有設計的四大原則幫忙啊。后來一個計算所的師兄還問我把這張片子討了去,說是只看一眼就喜歡上了里面的女孩,還一個勁的問我她是誰,是咱所的么,我趕緊解釋說不是不是……
最近一次為街舞社的表演做的PPT,發現自己的風格算是定型了,怎么看都似曾相識,當然也可以說,是黔驢技窮了……
按:系列文章,將談及Google Gadget的體系結構和開發入門,后期還會有和類似技術Mozilla Prism的對比。
開始之前先澄清一件事,這里所說的Gadget實際上是指Google Desktop Gadget,而不是指在iGoogle或者FaceBook上運行的Gadget,那個叫做Universal Gadget。 細說起來,其實Gadget和Universal Gadget不僅名稱不同,在實現上也完全是兩回事。從使用者的角度看,Univeral Gadget就是一個HTML的頁面,只不過在顯示的時候是實時從iGoogle之類的容器網站上下載過來并展現在一個iframe里面的。而Desktop Gadget則是一個不折不扣的桌面應用程序(雖然運行在Google Desktop這個容器中)。 從開發人員的角度看,Universal Gadget是一個JavaScript文件和XML文件的集合,由容器網站(例如iGoogle,Facebook等等)來渲染成HTML頁面并呈現給最終用戶。在一個Universal Gadget中使用的技術都是標準的Web技術,其能量也限制在瀏覽器的框架中。 而在開發一個Desktop Gadget時,雖然也使用XML文件來指定程序的UI,使用JavaScript來實現程序的邏輯,但與Web或者瀏覽器都沒有天然的聯系,說是完全的另一套程序開發體系也不為過(使用的XML語法與Universal Gadget不同,能夠使用的JavaScript的對象及功能也不同)。 但另一方面的情況導致兩者時常被人混為一談,那就是,一個Universal Gadget是可以被加載到Desktop Gadget的面板中并正常運行的(嚴格的說只有一部分),而一部分Desktop Gadget也可以加載到iGoogle網站中運行(因而使它看上去像一個Universal Gadget,當然需要系統已安裝了Google Desktop才可以)。 書歸正傳,下面就來說Gadget到底是什么,以及它的體系結構和背后思想。 (注:以下如果沒有特別指明,提起Gadget全都是指Desktop Gadget,而iGoogle上的Gadget會指明為Universal Gadget)
Java號稱對Unicode提供天然的支持,這話在很久很久以前就已經是假的了(不過曾經是真的),實際上,到JDK5.0為止,Java才算剛剛跟上Unicode的腳步,開始提供對增補字符的支持。
現在的Unicode碼空間為U+0000到U+10FFFF,一共1114112個碼位,其中只有1,112,064 個碼位是合法的(我來替你做算術,有2048個碼位不合法),但并不是說現在的Unicode就有這么多個字符了,實際上其中很多碼位還是空閑的,到Unicode 4.0 規范為止,只有96,382個碼位被分配了字符(但無論如何,仍比很多人認為的65536個字符要多得多了)。其中U+0000 到U+FFFF的部分被稱為基本多語言面(Basic Multilingual Plane,BMP)。U+10000及以上的字符稱為補充字符。在Java中(Java1.5之后),補充字符使用兩個char型變量來表示,這兩個char型變量就組成了所謂的surrogate pair(在底層實際上是使用一個int進行表示的)。第一個char型變量的范圍稱為“高代理部分”(high-surrogates range,從"uD800到"uDBFF,共1024個碼位), 第二個char型變量的范圍稱為low-surrogates range(從"uDC00到"uDFFF,共1024個碼位),這樣使用surrogate pair可以表示的字符數一共是1024的平方計1048576個,加上BMP的65536個碼位,去掉2048個非法的碼位,正好是1,112,064個碼位。 關于Unicode的碼空間實際上有一些稍不小心就會讓人犯錯的地方。比如我們都知道從U+0000到U+FFFF的部分被稱為基本多語言面(Basic Multilingual Plane,BMP),這個范圍內的字符在使用UTF-16編碼時,只需要一個char型變量就可以保存。仔細看看這個范圍,應該有65536這么大,因此你會說單字節的UTF-16編碼能夠表示65536個字符,你也會說Unicode的基本多語言面包含65536個字符,但是再想想剛才說過的surrogate pair,一個UTF-16表示的增補字符(再一次的,需要兩個char型變量才能表示的字符)怎樣才能被正確的識別為增補字符,而不是兩個普通的字符呢?答案你也知道,就是通過看它的第一個char是不是在高代理范圍內,第二個char是不是在低代理范圍內來決定,這也意味著,高代理和低代理所占的共2048個碼位(從0xD800到0xDFFF)是不能分配給其他字符的。 但這是對UTF-16這種編碼方法而言,而對Unicode這樣的字符集呢?在Unicode的編號中,U+D800到U+DFFF是否有字符分配?答案是也沒有!這是典型的字符集為方便編碼方法而做的安排(你問他們這么做的目的?當然是希望基本多語言面中的字符和一個char型的UTF-16編碼的字符能夠一一對應,少些麻煩,從中我們也能看出UTF-16與Unicode間很深的淵源與結合)。也就是說,無論Unicode還是UTF-16編碼后的字符,在0x0000至0xFFFF這個范圍內,只有63488個字符。這就好比最初的CPU被勉強拿來做多媒體應用,用得多了,CPU就不得不修正自己從硬件上對多媒體應用提供支持了。 盡管不情愿,但說到這里總還得扯扯相關的概念:代碼點和代碼單元。 代碼點(Code Point)就是指Unicode中為字符分配的編號,一個字符只占一個代碼點,例如我們說到字符“漢”,它的代碼點是U+6C49。 代碼單元(Code Unit)則是針對編碼方法而言,它指的是編碼方法中對一個字符編碼以后所占的最小存儲單元。例如UTF-8中,代碼單元是一個字節,因為一個字符可以被編碼為1個,2個或者3個4個字節;在UTF-16中,代碼單元變成了兩個字節(就是一個char),因為一個字符可以被編碼為1個或2個char(你找不到比一個char還小的UTF-16編碼的字符,嘿嘿)。說得再羅嗦一點,一個字符,僅僅對應一個代碼點,但卻可能有多個代碼單元(即可能被編碼為2個char)。 以上概念絕非學術化的繞口令,這意味著當你想以一種統一的方式指定自己使用什么字符的時候,使用代碼點(即你告訴你的程序,你要用Unicode中的第幾個字符)總是比使用代碼單元更好(因為這樣做的話你還得區分情況,有時候提供一個16進制數字,有時候要提供兩個)。 例如我們有一個增補字符???(哈哈,你看到了三個問號對吧?因為我的系統顯示不出這個字符),它在Unicode中的編號是U+2F81A,當在程序中需要使用這個字符的時候,就可以這樣來寫: String s=String.valueOf(Character.toChars(0x2F81A));
char[]chars=s.toCharArray(); for(char c:chars){ System.out.format("%x",(short)c); } 后面的for循環把這個字符的UTF-16編碼打印了出來,結果是 d87edc1a 注意到了嗎?這個字符變成了兩個char型變量,其中0xd87e就是高代理部分的值,0xdc1a就是低代理的值。
如果你是JVM的設計者,讓你來決定JVM中所有字符的表示形式,你會不會允許使用各種編碼方式的字符并存?
我想你的答案是不會,如果在內存中的Java字符可以以GB2312,UTF-16,BIG5等各種編碼形式存在,那么對開發者來說,連進行最基本的字符串打印、連接等操作都會寸步難行。例如一個GB2312的字符串后面連接一個UTF-8的字符串,那么連接后的最終結果應該是什么編碼的呢?你選哪一個都沒有道理。 因此牢記下面這句話,這也是Java開發者的共同意志:在Java中,字符只以一種編碼形式存在,那就是UTF-16。 但“在Java中”到底是指在哪里呢?就是指在JVM中,在內存中,在你的代碼里聲明的每一個char,String類型的變量中。例如你在程序中這樣寫 char han='漢';
在內存的相應區域,這個字符就表示為0x6C49。可以用下面的代碼證明一下: char han='漢';
System.out.format("%x",(short)han); 輸出是: 6c49 反過來用UTF-16編碼來指定一個字符也可以,像這樣: char han=0x6c49;
System.out.println(han); 輸出是: 漢 這其實也是說,只要你正確的讀入了“漢”這個字,那么它在內存中的表示形式一定是0x6C49,沒有任何其他的值能代表這個字(當然,如果你讀錯了,那結果是什么就不知道了,范偉說:讀,讀錯了呀,那還等于好幾億呢;本山大哥說:好幾億你也沒答上,請聽下一題)。 JVM的這種約定使得一個字符存在的世界分為了兩部分:JVM內部和OS的文件系統。在JVM內部,統一使用UTF-16表示,當這個字符被從JVM內部移到外部(即保存為文件系統中的一個文件的內容時),就進行了編碼轉換,使用了具體的編碼方案(也有一種很特殊的情況,使得在JVM內部也需要轉換,不過這個是后話)。 因此可以說,所有的編碼轉換就只發生在邊界的地方,JVM和OS的交界處,也就是你的各種輸入輸出流(或者Reader,Writer類)起作用的地方。 話頭扯到這里就必須接著說Java的IO系統。 盡管看上去混亂繁雜,但是所有的IO基本上可以分為兩大陣營:面向字符的Reader啊Wrtier啊,以及面向字節的輸入輸出流。 下面我來逐一分解,其實一點也不難。 面向字符和面向字節中的所謂“面向”什么,是指這些類在處理輸入輸出的時候,在哪個意義上保持一致。如果面向字節,那么這類工作要保證系統中的文件二進制內容和讀入JVM內部的二進制內容要一致。不能變換任何0和1的順序。因此這是一種非常“忠實于原著”的做法(偶然間讓我想起郭敬明抄襲莊羽的文章,那家伙,太忠實于原著了,笑)。 這種輸入輸出方式很適合讀入視頻文件或者音頻文件,或者任何不需要做變換的文件內容。 而面向字符的IO是指希望系統中的文件的字符和讀入內存的“字符”(注意和字節的區別)要一致。例如我們的中文版WindowsXP系統上有一個GBK的文本文件,其中有一個“漢”字,這個字的GBK編碼是0xBABA(而UTF-16編碼是0x6C49),當我們使用面向字符的IO把它讀入內存并保存在一個char型變量中時,我希望IO系統不要傻傻的直接把0xBABA放到這個char型變量中,我甚至都不關心這個char型變量具體的二進制內容到底是多少,我只希望這個字符讀進來之后仍然是“漢”這個字。 從這個意義上也可以看出,面向字符的IO類,也就是Reader和Writer類,實際上隱式的為我們做了編碼轉換,在輸出時,將內存中的UTF-16編碼字符使用系統默認的編碼方式進行了編碼,而在輸入時,將文件系統中已經編碼過的字符使用默認編碼方案進行了還原。我兩次提到“默認”,是說Reader和Writer的聰明也僅此而已了,它們只會使用這個默認的編碼來做轉換,你不能為一個Reader或者Writer指定轉換時使用的編碼。這也意味著,如果你使用中文版WindowsXP系統,而上面存放了一個UTF-8編碼的文件,當你使用Reader類來讀入的時候,它會傻傻的使用GBK來做轉換,轉換后的內容當然驢唇不對馬嘴! 這種笨,有時候其實是一種傻瓜式的功能提供方式,對大多數初級用戶(以及不需要跨平臺的高級用戶)來說反而是件好事。 但我們不一樣啦,我們都是國家棟梁,肩負著趕英超美的責任,必須師夷長技以治夷,所以我們總還要和GBK編碼以外的文件打交道。 說了上面這些內容,想必聰明的讀者已經看出來,所謂編碼轉換就是一個字符與字節之間的轉換,因此Java的IO系統中能夠指定轉換編碼的地方,也就在字符與字節轉換的地方,那就是(讀者:InputSteamReader和OutputStreamWriter!作者:太強了,都會搶答了?。?br /> 這兩個類是字節流和字符流之間的適配器類,因此他們肩負著編碼轉換的任務簡直太自然啦!要注意,實際上也只能在這兩類實例化的時候指定編碼,是不是很好記呢? 下面來寫一段小程序,來把“漢”字用我們非常崇拜的UTF-8編碼寫到文件中! try{
PrintWriter out=new PrintWriter(new OutputStreamWriter(new FileOutputStream("c:/utf-8.txt"),"UTF-8")); try{ out.write("漢"); }finally{ out.close(); } }catch(IOException e){ throw new RuntimeException(e); } 運行之后到c盤下去找utf-8.txt這個文件,用UltraEdit打開,使用16進制查看,看到了什么?它的值是0xE6B189?。ㄟ@正是“漢”這個字的UTF-8編碼)噢耶?。ㄗx者:這,這有什么好高興的……) 下一節我們來看看實現這種操作的其他方式,讀到這里,你已經基本上是字符編碼的高手了哦。 |
|