最近公司的Android項目須要用到攝像頭作條碼或二維碼的掃描,Google一下,發現一個以Apache License 2.0 開源的 ZXing項目。Zxing項目裏的Android實現太過複雜多餘東西太多,得對其進行簡化。html
下載源代碼:點擊這裏android
編譯核心庫:Zxing的主頁上有介紹具體步驟,你們也能夠參照這篇博文:android 條碼識別軟件開發全解析(續2詳解絕殺!)web
打開Eclipse 導入 源碼中的 Android 項目,而後右擊項目 選擇「Build path」——》"Add External Archives" 把核心庫 core.jar文件加入到項目中。apache
此時編譯一下項目,會發現報錯,「 Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?」之類的。打開raw 下的Values 發現錯誤是在一個<String>上。這裏把 「preferences_custom_product_search_summary」 裏的 %s %f 所有都改爲 %1$s %1$f(由於咱們用不到多國語言,建議只保留默認的Value ,其餘所有刪除)。app
緣由:因爲新的SDK採用了新版本的aapt(Android項目編譯器),這個版本的aapt編譯起來會比老版本更加的嚴格,而後在Android最新的開發文檔的描述String的部分,已經說明如何去設置 %s 等符號ide
「If you need to format your strings using String.format(String, Object...) , then you can do so by putting your format arguments in the string resource. For example, with the following resource:佈局
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>ui
In this example, the format string has two arguments: %1$s is a string and %2$d is a decimal number. You can format the string with arguements from your application...「this
通過以上步驟後項目應該就能夠運行了。google
可是ZXing的android項目東西太多了,有不少是咱們不須要的,得新建另外一個項目簡化它。
在開始前大體介紹一下簡化ZXing須要用到各個包 、類的職責。
CaptureActivity。這個是啓動Activity 也就是掃描器(若是是第一安裝,它還會跳轉到幫助界面)。
CaptureActivityHandler 解碼處理類,負責調用另外的線程進行解碼。
DecodeThread 解碼的線程。
com.google.zxing.client.android.camera 包,攝像頭控制包。
ViewfinderView 自定義的View,就是咱們看見的拍攝時中間的框框了。
新建另外一個項目將啓動的Activity命名爲CaptureActivity,並導入核心庫。項目新建完成後咱們打開 CaptureActivity 的佈局文件,我這裏爲main。把裏面的XML修改成:
1 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="fill_parent" android:layout_height="fill_parent">
3 <SurfaceView android:id="@+id/preview_view"
4 android:layout_width="fill_parent" android:layout_height="fill_parent"
5 android:layout_centerInParent="true"/>
6
7 <com.Zxing.Demo.view.ViewfinderView
8 android:id="@+id/viewfinder_view" android:layout_width="fill_parent"
9 android:layout_height="fill_parent" android:background="@android :color/transparent"/>
10 <TextView android:layout_width="wrap_content"
11 android:id="@+id/txtResult"
12 android:layout_height="wrap_content" android:text="@string/hello"/>
13
14 </FrameLayout>
能夠看到在XML裏面用到了 ViewfinderView 自定義view 。因此新建一個View 的包,而後把:ViewfinderView 和 ViewfinderResultPointCallback 靠到裏面(記得對應修改XML裏面的包)。
打開 CaptureActivity 覆蓋 onCreate 方法:
1 @Override
2 publicvoid onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 setContentView(R.layout.main);
5 //初始化 CameraManager
6 CameraManager.init(getApplication());
7
8 viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
9 txtResult = (TextView) findViewById(R.id.txtResult);
10 hasSurface =false;
11 inactivityTimer =new InactivityTimer(this);
12 }
這裏調用到的 CameraManager 類是控制攝像頭的包裏的類。新建一個camera包把:com.google.zxing.client.android.camera 裏面的類所有拷入,另外我把PlanarYUVLuminanceSource也拷入到這個包裏面。根據錯誤的提示來修正代碼,主要是修改正包結構。(整個簡化的流程都是如此:「根據錯誤提示,修改代碼」)。
在修改的過程當中,有不少是關於R 資源的問題,在此咱們須要將Values 裏面的兩個xml資源文件拷入項目中:colos.xml 和ids.xml 。 ctrl+b 一下看看error 是否是少了不少。在CameraManager中有些地方須要用到項目的配置,這裏須要把配置直接寫入代碼中:
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
//是否使用前燈
// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
// FlashlightManager.enableFlashlight();
// }
FlashlightManager.enableFlashlight();
使用攝像頭須要加入相應的權限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
當View 和 camera 包裏的錯誤修正完成後,咱們繼續來看CaptureActivity。
覆蓋onResume方法初始化攝像頭:
@Override
protectedvoid onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats =null;
characterSet =null;
playBeep =true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep =false;
}
initBeepSound();
vibrate =true;
}
initCamera
SurfaceHolder接口實現
initCamera () 方法用於初始化攝像頭,若是排除了全部的error ,運行項目時就能夠看到大體掃描界面了。 surfaceHolder.addCallback(this);表示讓CaptureActivity實現其callback接口。
handler = new CaptureActivityHandler(this, decodeFormats,characterSet) 用於進行掃描解碼處理。
上面的步驟主要都是用於對攝像頭的控制,而解碼的真正工做入口是在CaptureActivityHandler 裏面的。新建一個Decoding包把如下文件拷入包中:
CaptureActivityHandler
DecodeFormatManager
DecodeHandler
DecodeThread
FinishListener
InactivityTimer
Intents
因爲咱們的包結構和Zxing 項目的有所不一樣因此須要注意一下類的可訪問性
一樣開始ctrl+B 編譯一下,而後開始修正錯誤。
在CaptureActivityHandler 裏 把 handleMessage 裏的部分方法先註釋掉如:「decode_succeeded 」分支,這是解碼成功時調用 CaptureActivity 展現解碼的結果。
在DecodeThread 類裏,修改部分涉及Preference配置的代碼:
DecodeThread(CaptureActivity activity,
Vector<BarcodeFormat> decodeFormats,
String characterSet,
ResultPointCallback resultPointCallback) {
this.activity = activity;
handlerInitLatch =new CountDownLatch(1);
hints =new Hashtable<DecodeHintType, Object>(3);
//// The prefs can't change while the thread is running, so pick them up once here.
// if (decodeFormats == null || decodeFormats.isEmpty()) {
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
// decodeFormats = new Vector<BarcodeFormat>();
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D, true)) {
// decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
// }
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) {
// decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
// }
// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) {
// decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
// }
// }
if (decodeFormats ==null|| decodeFormats.isEmpty()) {
decodeFormats =new Vector<BarcodeFormat>();
decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
}
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
if (characterSet !=null) {
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
}
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
}
這裏是設置 解碼的類型,咱們如今默認將全部類型都加入。
錯誤類型基本上都是:包結構、PreferencesActivity 的配置 、類可訪問性的問題。根據錯誤提示耐心把錯誤解決。
還記得在 CaptureActivityHandler 的 messagehandler 裏註銷掉的Case分支嗎?如今CaptureActivity 裏實現它。
publicvoid handleDecode(Result obj, Bitmap barcode) {
inactivityTimer.onActivity();
viewfinderView.drawResultBitmap(barcode);
playBeepSoundAndVibrate();
txtResult.setText(obj.getBarcodeFormat().toString() +":"
+ obj.getText());
}
ZXing的簡化已基本完成,有幾位是能夠運行成功的?呵呵。
下面是CaptureActivity的源碼:
CaputreActivity
簡化過的包結構圖:
簡化後的ZXing 更加方便咱們瞭解ZXing項目 是如何解碼的。只要仔細查看源碼,進行單點跟蹤調試,相信你們很容易能理解。
不少人留言要源碼, 其實我這不是什麼源碼,我只是把ZXing的東西簡化了一下而已。事實上我也不喜歡直接放源碼項目,這樣你們就不想讀ZXing的源碼了。
下面是我簡化的版本:Zxing簡化
不少人須要Core 核心包(其實ZXing的源碼裏面就有),這裏提供下我寫文章時的版本 1.6的: