Android自由行之走進zxing,輕鬆實現二維碼掃描

     如今不少App都集成了掃一掃功能,最經常使用的微信、QQ、手機助手等。二維碼也使得生活變得更加簡潔,掃一掃訂餐、掃一掃下載等等。那麼,說到二維碼,咱們不得不提Google一個開源的掃碼框架:zxing。android

     開源下載:http://code.google.com/p/zxing/數組

     zxing是基於多種1D/2D條碼處理的開源庫,是一個完整的項目。它能夠經過手機攝像頭實現條碼的掃描以及解碼,功能及其強大。那麼若是要實現二維碼的掃描以及解碼,咱們須要在該開源項目的基礎上進行簡化,並修改。讓咱們來看一下微信

二維碼掃描結果      

    上圖是仿照QQ的掃一掃進行修改的zxing項目,以zxing項目爲基礎,結合實際應用,這裏做了三點改變:框架

   (1)豎屏掃描ide

   (2)自定義取景框佈局

   (3)從新定義掃描結果處理ui

 

     1、第一步:下載zxing項目,並簡化出掃描框架this

     一、首先,下載最新zxing開源項目。 下載地址:http://code.google.com/p/zxing/google

     二、分析項目結構,明確掃描框架需求。在zxing中,有不少其餘的功能,項目結構比較複雜;針對二維碼QRCode掃描,咱們須要幾個包:編碼

        (1)com.google.zxing.client.android.Camera  基於Camera調用以及參數配置,核心包

        (2)DecodeFormatManager、DecodeThread、DecodeHandler 基於解碼格式、解碼線程、解碼結果處理的解碼類

        (3)ViewfinderView、ViewfinderResultPointCallBack 基於取景框視圖定義的View類

        (4)CaptureActivity、CaptureActivityHandler 基於掃描Activity以及掃描結果處理的Capture類

        (5)InactivityTimer、BeepManager、FinishListener 基於休眠、聲音、退出的輔助管理類

        (6)Intents、IntentSource、PrefrencesActivity 基於常量存儲的常量類

    三、新建工程,添加以下權限:  

  <uses-permission android:name="android.permission.CAMERA"/>
         <uses-permission android:name="android.permission.INTERNET" />
         <uses-permission android:name="android.permission.VIBRATE" />
         <uses-permission android:name="android.permission.FLASHLIGHT" />
permission

        添加core.jar文件,並BuildPath;將上述類或包加入工程後,會報一系列錯誤,緣由有幾點:

       (1)資源文件缺少,將zxing下須要的資源文件copy到新工程下

       (2)版本兼容問題,zxing下不少技術都是使用4.0版本及以上,集成到低版本以後,須作相應改動,詳情參照項目源碼

       (3)包結構引用問題,須要從新導入包引用

   四、簡化CapyureActivity, camera包以及decode各種異常解決之後,便可對CaptureActivity進行代碼簡化 ,首先看一下capture.xml佈局

 1 <merge xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools" >
 3 
 4     <!-- 總體透明畫布 -->
 5     <SurfaceView
 6         android:id="@+id/preview_view"
 7         android:layout_width="fill_parent"
 8         android:layout_height="fill_parent" />
 9 
10     <!-- 掃描取景框 -->
11     <com.karics.library.zxing.view.ViewfinderView
12         android:id="@+id/viewfinder_view"
13         android:layout_width="fill_parent"
14         android:layout_height="fill_parent" />
15     
16  <!-- 標題欄 -->
17     <RelativeLayout 
18         android:layout_width="fill_parent"
19         android:layout_height="50dp"
20         android:layout_gravity="top"
21         android:background="#99000000">
22         
23         <ImageButton
24             android:id="@+id/capture_imageview_back"
25             android:layout_width="42dp"
26             android:layout_height="42dp"
27             android:layout_centerVertical="true"
28             android:background="@drawable/selector_capture_back"/>
29         
30         <TextView 
31             android:layout_width="wrap_content"
32             android:layout_height="wrap_content"
33             android:layout_centerInParent="true"
34             android:textColor="#ffffffff"
35             android:textSize="20sp"
36             android:text="掃一掃"/>
37         
38     </RelativeLayout>
39 
40 </merge>
capture.xml

        capture.xml佈局去掉結果顯示,添加標題欄。那麼captureActivity中,onCreate(),onPause(),onResume(),onDestroy涉及到Camera的初始化或銷燬  

 1 @Override
 2     public void onCreate(Bundle icicle) {
 3         super.onCreate(icicle);
 4         // 保持Activity處於喚醒狀態
 5         Window window = getWindow();
 6         window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 7         setContentView(R.layout.capture);
 8 
 9         hasSurface = false;
10 
11         inactivityTimer = new InactivityTimer(this);
12         beepManager = new BeepManager(this);
13 
14         imageButton_back = (ImageButton) findViewById(R.id.capture_imageview_back);
15         imageButton_back.setOnClickListener(new View.OnClickListener() {
16 
17             @Override
18             public void onClick(View v) {
19                 finish();
20             }
21         });
22     }
23 
24     @Override
25     protected void onResume() {
26         super.onResume();
27 
28         // CameraManager必須在這裏初始化,而不是在onCreate()中。
29         // 這是必須的,由於當咱們第一次進入時須要顯示幫助頁,咱們並不想打開Camera,測量屏幕大小
30         // 當掃描框的尺寸不正確時會出現bug
31         cameraManager = new CameraManager(getApplication());
32 
33         viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
34         viewfinderView.setCameraManager(cameraManager);
35 
36         handler = null;
37 
38         SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
39         SurfaceHolder surfaceHolder = surfaceView.getHolder();
40         if (hasSurface) {
41             // activity在paused時但不會stopped,所以surface仍舊存在;
42             // surfaceCreated()不會調用,所以在這裏初始化camera
43             initCamera(surfaceHolder);
44         } else {
45             // 重置callback,等待surfaceCreated()來初始化camera
46             surfaceHolder.addCallback(this);
47         }
48 
49         beepManager.updatePrefs();
50         inactivityTimer.onResume();
51 
52         source = IntentSource.NONE;
53         decodeFormats = null;
54         characterSet = null;
55     }
56 
57     @Override
58     protected void onPause() {
59         if (handler != null) {
60             handler.quitSynchronously();
61             handler = null;
62         }
63         inactivityTimer.onPause();
64         beepManager.close();
65         cameraManager.closeDriver();
66         if (!hasSurface) {
67             SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
68             SurfaceHolder surfaceHolder = surfaceView.getHolder();
69             surfaceHolder.removeCallback(this);
70         }
71         super.onPause();
72     }
73 
74     @Override
75     protected void onDestroy() {
76         inactivityTimer.shutdown();
77         super.onDestroy();
78     }
View Code

        surfaceview是基於Camera而實現,surfaceview的使用須要實現SurfaceHolder.Callback接口,在開啓屏幕surfaceview的時候初始化camera

 1 @Override
 2     public void surfaceCreated(SurfaceHolder holder) {
 3         if (!hasSurface) {
 4             hasSurface = true;
 5             initCamera(holder);
 6         }
 7     }
 8 
 9     @Override
10     public void surfaceDestroyed(SurfaceHolder holder) {
11         hasSurface = false;
12     }
13 
14     @Override
15     public void surfaceChanged(SurfaceHolder holder, int format, int width,
16             int height) {
17 
18     }
View Code

        接下來看如何初始化Camera,代碼簡化以後以下

 1 private void initCamera(SurfaceHolder surfaceHolder) {
 2         if (surfaceHolder == null) {
 3             throw new IllegalStateException("No SurfaceHolder provided");
 4         }
 5         if (cameraManager.isOpen()) {
 6             return;
 7         }
 8         try {
 9             // 打開Camera硬件設備
10             cameraManager.openDriver(surfaceHolder);
11             // 建立一個handler來打開預覽,並拋出一個運行時異常
12             if (handler == null) {
13                 handler = new CaptureActivityHandler(this, decodeFormats,
14                         decodeHints, characterSet, cameraManager);
15             }
16         } catch (IOException ioe) {
17             Log.w(TAG, ioe);
18             displayFrameworkBugMessageAndExit();
19         } catch (RuntimeException e) {
20             Log.w(TAG, "Unexpected error initializing camera", e);
21             displayFrameworkBugMessageAndExit();
22         }
23     }
View Code

       在CaptureActivity中,有一個核心方法,用來返回並處理解碼結果,也即掃描結果。handleDecode(),若是須要對解碼後的內容進行本身的處理,須要對該方法進行改動,這裏修改        爲將解碼的bitmap以及內容回傳到開啓掃描的Activiity進行處理。

 1 public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
 2         inactivityTimer.onActivity();
 3 
 4         boolean fromLiveScan = barcode != null;
 5         //這裏處理解碼完成後的結果,此處將參數回傳到Activity處理
 6         if (fromLiveScan) {
 7             beepManager.playBeepSoundAndVibrate();
 8 
 9             Toast.makeText(this, "掃描成功", Toast.LENGTH_SHORT).show();
10 
11             Intent intent = getIntent();
12             intent.putExtra("codedContent", rawResult.getText());
13             intent.putExtra("codedBitmap", barcode);
14             setResult(RESULT_OK, intent);
15             finish();
16         }
17 
18     }
View Code

   五、將指定Url生成二維碼

 1 /**
 2      * 生成QRCode(二維碼)
 3      * 
 4      * @param str
 5      * @return
 6      * @throws WriterException
 7      */
 8     public static Bitmap createQRCode(String url) throws WriterException {
 9 
10         if (url == null || url.equals("")) {
11             return null;
12         }
13 
14         // 生成二維矩陣,編碼時指定大小,不要生成了圖片之後再進行縮放,這樣會模糊致使識別失敗
15         BitMatrix matrix = new MultiFormatWriter().encode(url,
16                 BarcodeFormat.QR_CODE, 300, 300);
17 
18         int width = matrix.getWidth();
19         int height = matrix.getHeight();
20 
21         // 二維矩陣轉爲一維像素數組,也就是一直橫着排了
22         int[] pixels = new int[width * height];
23 
24         for (int y = 0; y < height; y++) {
25             for (int x = 0; x < width; x++) {
26                 if (matrix.get(x, y)) {
27                     pixels[y * width + x] = 0xff000000;
28                 }
29 
30             }
31         }
32 
33         Bitmap bitmap = Bitmap.createBitmap(width, height,
34                 Bitmap.Config.ARGB_8888);
35         bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
36         return bitmap;
37     }
View Code

 

       經過以上四步,zxing項目的簡化工做基本完成。至於一些類須要進行小修小改,但願能夠對着源碼進行,這裏再也不贅述。二維碼掃描的總體構架主要包含三部分:

       一、定義取景框,也即掃描的View,經過surfaceview進行繪製

       二、Camera, 掃描的核心在於camera的配置使用,包括預覽,自動聚焦,打開設備等處理

       三、Decode解碼,掃描完成後整個工程的核心

       除去以上三個模塊,須要明確的就是CaptureActivitiy中handleDeCode()方法作本身的處理。

       點擊下面連接,便可下載源碼

       http://pan.baidu.com/s/1c0yADUS 

相關文章
相關標籤/搜索