1. 程式人生 > >Android掃描條形碼實現

Android掃描條形碼實現

掃描和識別條形碼的android支援庫

android支援掃描條形碼。不過手裡沒有真機,無法理解該功能是一個應用,還是一個可API呼叫的功能。不過在網上找到一個免費開源的android處理條形碼的庫。見:

可以作為應用安裝到android上,見:

或者,在另外的程式中使用該功能:

已經有java的第三方開源條形碼識別庫(zxing)了,在自己的應用中掃描條形碼實際上只需呼叫該第三方庫的實現即可。寫掃描和識別條形碼的android支援庫的時候還沒有g1手機,現在可以試試了。

為了能呼叫zxing,需要先安裝google market中的barcode scanner:

為了能呼叫zxing,需要先安裝google market中的barcode scanner:

程式很簡陋,第一個介面:

第二個介面,跳到了zxing庫提供的Activity去做條形碼掃描,這裡掃描的是生成QR二維條碼中的本站url。

qr碼瞬間就可以識別到,然後從zxing的Activity返回到程式中,顯示條目內容:

編寫程式碼很簡單。

java程式碼:

package com.easymorse;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ShowBarcodeActivity extends Activity implements OnClickListener {

private Button button;

private TextView textView;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

this.button = (Button) this.findViewById(R.id.Button01);
this.button.setOnClickListener(this);

this.textView = (TextView) this.findViewById(R.id.hello);
}

@Override
public void onClick(View view) {
Intent intent = new Intent(“com.google.zxing.client.android.SCAN”);
intent.putExtra(“SCAN_MODE”, “QR_CODE_MODE”);
this.startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != 0) {
return;
}

this.textView.setText(data.getStringExtra(“SCAN_RESULT”));
}
}

layout目錄下面的main.xml:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
>
<TextView
android:id=”@+id/hello”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
/>

<Button android:text=”@string/Button01″ android:id=”@+id/Button01″ android:layout_width=”wrap_content” android:layout_height=”wrap_content”></Button>
</LinearLayout>

values目錄下的strings.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<resources>
<string name=”hello”>目前沒有掃描結果。</string>
<string name=”app_name”>購物小參考</string>
<string name=”Button01″>點選開始掃描…</string>
</resources>

從這個示例中也可以看到android強大的地方,可以複用(reuse)其他應用的服務(Service)或者Activity。

剛才寫的編寫最簡單的android掃描條形碼功能,只能掃描qr二維碼。如果要掃描普通的商品條形碼,需要在該程式上座一點兒修改。

介面的改動:

掃描條形碼時的效果:

顯示掃描結果(識別速度明顯低於二維碼):

程式碼的改動:

package com.easymorse;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ShowBarcodeActivity extends Activity {

private Button button;

private Button button2;

private TextView textView;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

this.button = (Button) this.findViewById(R.id.Button01);
this.button.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Intent intent = new Intent(
“com.google.zxing.client.android.SCAN”);
intent.putExtra(“SCAN_MODE”, “QR_CODE_MODE”);
startActivityForResult(intent, 0);
}
});

this.button2 = (Button) this.findViewById(R.id.Button02);
this.button2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Intent intent = new Intent(
“com.google.zxing.client.android.SCAN”);
intent.putExtra(“SCAN_MODE”, “EAN_13″);
startActivityForResult(intent, 0);
}
});

this.textView = (TextView) this.findViewById(R.id.hello);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != 0) {
return;
}

this.textView.setText(data.getStringExtra(“SCAN_RESULT”));
}
}

寫完這個示例,才發現其實現在不需要寫:

intent.putExtra(“SCAN_MODE”, “QR_CODE_MODE”);

zxing現在的庫可以自動的識別是哪種編碼。如果寫了,反而是指定型別,不能識別其他型別的編碼。實際上,上面的:

intent.putExtra(“SCAN_MODE”, “EAN_13″);

寫的不對。具體常量引數,見:

上面的內容不修改了,正確的原始碼共享在google code的svn中,見:

寫了個技術原型,通過手機識別圖書的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庫包含在自己的程式中,這比較適合正式的應用。

本文使用的是前者。

示例程式碼

示例原始碼見:

還有一個更為簡單的示例,可用於理解呼叫zxing和豆瓣API的基本過程,見:

實現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);

這裡要注意,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官方網址:

這裡僅用到根據ISBN得到圖書資訊xml的功能。在這裡:

如果匿名使用這個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。