識別圖書ISBN號並輸出查詢結果的樣本
寫了個技術原型,通過手機識別圖書的ISBN條碼,然後通過豆瓣網的API,輸出ISBN號對應的圖書資訊。
樣本很簡單,上來是個按鈕,點擊後,進入zxing的activity,用於掃描圖書ISBN號。
使用zxing的掃描功能,這次還出現了點兒問題。我的nexus one網路攝影機比較髒了,造成對焦後無法識別,或者識別的很慢。嚇了我一跳,因為以前沒有問題的。
更換了幾個版本zxing,問題依舊,擦拭了鏡頭,問題立刻得到瞭解決。
nexus one的像素和自動對焦能力,也可能包括cpu的處理因素,稍稍對中條碼,立刻得到結果。之前的G1就要慢一些。
這也帶來了捕獲上面的困難,因為一瞬間掃描就結束了。怎麼辦呢?我用手在鏡頭上揉搓了幾下,就可以了,哈哈。
提示找到圖書後,通過豆瓣API,擷取到圖書的XML,這裡要說,豆瓣的圖書資訊還是比較全的,我用國外圖書也找到了資訊。
因為使用聯通WCDMA卡,整個過程很快。
下面說說代碼中的一些要點。
如何掃描條碼
現在做條碼掃描,java一般用zxing。zxing有多種複用方式,最簡單的方式是,安裝zxing軟體,通過google market。在自己的應用中,通過Intent調用zxing的掃描條碼的Activity,然後將掃描到的結果返回自己的Activity。
比較複雜的辦法是,將zxing庫包含在自己的程式中,這比較適合正式的應用。
本文使用的是前者。
範例程式碼
樣本原始碼見:
http://easymorse.googlecode.com/svn/tags/sou.book-0.2
還有一個更為簡單的樣本,可用於理解調用zxing和豆瓣API的基本過程,見:
http://easymorse.googlecode.com/svn/tags/sou.book-0.1
實現activity跳轉
在0.2版本中,涉及到3個activity,首先是搜尋圖書的activity,見最上面的圖。點擊按鈕後,進入zxing的activity,從視覺上看,zxing擷取到ISBN號後,進入了第三個activity,顯示圖書的詳細資料。
其實,從zxing得到ISBN號以後,還是返回到搜尋圖書的activity,但是該activity馬上發起新的intent,進入新的activity。另外,這裡也示範了如果通過intent共用activity之間的參數。
以下代碼建立新的activity,並設定參數:
Intent intent = new Intent();
intent.setClass(this, SearchBookActivity.class);
intent.putExtra("ISBN", data.getExtras().getString("SCAN_RESULT"));
this.startActivity(intent);
以下是從intent中擷取到參數:
this.getIntent().getExtras().getString("ISBN");
有關zxing的使用,可以參考我另外一個樣本,android掃描商品條碼。
webview使用本地頁面的考慮
在顯示圖書查詢結果的activity中,使用webview,這樣顯示可以做的比較靈活和簡單。
顯示頁面有多種做法,比如可以是遠程伺服器上的頁面,這要求有伺服器端的支援。看起來這樣比較省心,尤其是比如有iphone,有android等等異構終端的情況下。手機開發人員可以將這部分都空出來,交給伺服器端去解決。而且是統一的解決方案。
但是,實踐中,有兩個問題:
- 伺服器端開發人員要為不同手機用戶端實現不同的樣式,而且伺服器端開發人員往往並不配備手機裝置,這也確實是難以做到的,難道要為一個做適配的伺服器端開發人員配備iphone、android和s60手機嗎?
- 手機終端開發人員無法靈活的控制webview中的細節,webview(這裡指android環境,iphone環境類似的)支援通過javascript訪問手機的服務或者對象,但這是平台相關的,android有自己的代碼,iphone也有自己的代碼,很難在伺服器端統一解決。
我在這個原型中試圖解決,即,HTML頁面在手機端本地,手機端和伺服器互動,只是取得資料,而不包含顯示樣式(也就是HTML頁面的內容),手機端通過javascript,將取得的資料注入到本地頁面中顯示。
這種方式的好處是:
- 手機端和伺服器端互動是擷取資料,這是各種手機系統都通用的;
- 這種方式類似與AJAX,對資料流量的要求也會下降;
- 手機端開發人員可以靈活的決定頁面的樣式和互動動作。
在本例中,將html和css放置在項目的assets目錄下,另外,該目錄下,還有一張圖片,用於未載入到圖書圖片時佔位。想像一下,如果不使用webview,這個需求需要編程處理,是比較繁瑣的,可參見android非同步載入ListView中的圖片中的處理。
實現webview的代碼很簡單,這是一個更簡單的樣本,見android編寫簡單的Webview。本例相關代碼:
this.resultWeb = (WebView) this.findViewById(R.id.resultWeb);
this.resultWeb.getSettings().setSupportZoom(false);
this.resultWeb.getSettings().setJavaScriptCanOpenWindowsAutomatically(
true);
this.resultWeb.getSettings().setJavaScriptEnabled(true);
this.resultWeb.loadUrl(file:///android_asset/results.html);
這裡要注意,setJavaScriptEnabled啟用javascript,否則javascript無效。
android java與html頁面中javascript互操作
javascript和java之間的通訊問題,本例只通過javascript調用java,還是比較簡單的。如果是java調用javascript,應該需要啟動單獨的線程來實現。
需要為webview對象建立一個javascript調用的介面對象:
this.resultWeb.addJavascriptInterface(new Object() {
public String getBookName() {
return bookInfo.getName();
}
public String getBookSummary() {
return bookInfo.getSummary();
}
public String getBookImageUrl() {
return bookInfo.getImageUrl();
}
public String getBookAuthor() {
return bookInfo.getAuthor();
}
}, "searchResult");
其中第一個參數是javascript調用的對象,第二個參數,是調用對象的名稱。
看看javascript中怎樣調用這個對象:
document.getElementById("bookName").innerHTML=window.searchResult.getBookName();
可以看到window對象多了個searchResult對象屬性,這個searchResult就是上面方法中第二個參數。通過該名字,就調用到了上面第一個參數建立的內部類對象。
使用豆瓣API
豆瓣API官方網址:
http://www.douban.com/service/apidoc/
這裡僅用到根據ISBN得到圖書資訊xml的功能。在這裡:
http://www.douban.com/service/apidoc/reference/subject#擷取書籍資訊
如果匿名使用這個API,有一定的限制:
API調用被限制為每分鐘請求不超過10次。
這裡的調用次數是每ip地址的。對於手機應用來說,大於6秒鐘掃描一次圖書,應該是夠用了。
解析豆瓣XML查詢結果
對豆瓣的xml查詢結果,使用了android內建的xmlpull api進行解析。
這裡和android通過xmlpull解析xml稍有差別在於,後者是擷取xml的屬性值,這裡是擷取節點中的常值內容。
需要:
if (i == XmlPullParser.START_TAG
&& parser.getName().equals("attribute")
&& parser.getAttributeValue(0).equals("title")) {
bookInfo.setName(parser.nextText());
Log.v("soubook", "title>>" + bookInfo.getName());
continue;
}
通過parser.nextText()方法擷取。另外,要注意在擷取完畢後,continue結束本次迴圈。
HTML與CSS
本例中顯示查詢結果的webview使用了比較難看的灰色背景,主要是為了示範外部css可以很好的和html結合使用。
同理,也應該可以在html頁面中引用外部的javascript,打算在下一個版本中加入jquery。