條碼掃描二維碼掃描——ZXing android 源碼簡化

前言

  最近公司的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的:

Zxing

相關文章
相關標籤/搜索