一、二維碼與條形碼工作原理
目前的很多應用上都有掃碼功能,當時微信推出二維碼掃碼功能時,覺得imagine,通過一張簡單的圖片就能掃描添加還有,還有分享名片功能(也是一張二維碼圖片,識別掃描)。
下面小編將通過文章主要介紹QRCode方面技術.
QRCode是被廣泛應用的一種二維碼,解碼速度快。二維碼相對於條形碼來說,二維碼的存儲數據量更大,空間利用率高,有一定的容錯性。
二維碼原理介紹:
二維碼是用某種特定的幾何圖形按一定的規律在平面上分佈的黑白相間的圖形記錄數據符號信息的;
在代碼編制上巧妙的利用構成計算機內部邏輯基礎的0/1比特流的概念,使用若干個與二進制相對應的幾何形體來表示文字數值信息,通過圖像輸入設備或光電掃描設備自動識讀以實現信息自動處理;
二維碼能夠在橫向和縱向兩個方位同時表達信息,因此能在很小的面積內表達大量的信息;
二維碼相對於條形碼的優勢就是省空間;
一、二維碼與條形碼工作原理目前的很多應用上都有掃碼功能,當時微信推出二維碼掃碼功能時,覺得imagine,通過一張簡單的圖片就能掃描添加還有,還有分享名片功能(也是一張二維碼圖片,識別掃描)。
下面小編將通過文章主要介紹QRCode方面技術.
QRCode是被廣泛應用的一種二維碼,解碼速度快。二維碼相對於條形碼來說,二維碼的存儲數據量更大,空間利用率高,有一定的容錯性。
二維碼原理介紹:
二維碼是用某種特定的幾何圖形按一定的規律在平面上分佈的黑白相間的圖形記錄數據符號信息的;
在代碼編制上巧妙的利用構成計算機內部邏輯基礎的0/1比特流的概念,使用若干個與二進制相對應的幾何形體來表示文字數值信息,通過圖像輸入設備或光電掃描設備自動識讀以實現信息自動處理;
二維碼能夠在橫向和縱向兩個方位同時表達信息,因此能在很小的面積內表達大量的信息;
二維碼相對於條形碼的優勢就是省空間;
QRCode基本結構
上圖是一個QRCode的基本結構:
位置探測圖形、位置探測圖形分隔符、定位圖形:用於二維碼的定位,對每個QR來說,位置都是固定存在的,只是大小規格有所差異;
校正圖形:確定規格,校正圖形的數量和位置也就確定了;
格式信息:表示二維碼的糾錯級別,分爲L、M、Q、H;
版本信息:即二維碼的規格,QR碼符號共有40種規格的矩陣;
數據和糾錯碼字:實際保存的二維碼信息,和糾錯碼字(用於修正二維碼損壞帶來的錯誤)。
條形碼原理介紹:
條形碼掃描器結構
由於不同顏色的物體,其反射的可見光的波長不同,所以當條形碼掃描器光源發出的光經光闌及凸透鏡1後,照射到黑白相間的條形碼上時,反射光經凸透鏡2聚焦後,照射到光電轉換器上,於是光電轉換器接受到與白條和黑條相對應的強弱不同的反射光信號,並轉換成相應的電信號輸出到放大整形電路,整形電路把模擬信號轉換成數字電信號,再經譯碼接口電路譯成數字字符信息。
整形電路的脈衝數字信號經譯碼器譯成數字、字符信息.它通過識別起始、終止字符來判別出條形碼符號的碼制及掃描方向;通過測量脈衝數字電信號0、1的數目來判別出條和空的數目.通過測量0、1信號持續的時間來判別條和空的寬度.這樣便得到了被辯讀的條形碼符號的條和空的數目及相應的寬度和所用碼制,根據碼制所對應的編碼規則,便可將條形符號換成相應的數字、字符信息,通過接口電路送給計算機系統進行數據處理與管理,便完成了條形碼辨讀的全過程。
二、Zxing使用原理介紹
Zxing是一個開源的,用於Java實現的多種格式的1D/2D條碼圖像處理庫,它包含了聯繫到其他語言的接口。
Zxing可以實現使用手機的內置攝像頭完成條形碼和二維碼的掃描與解碼。
Zxing可以實現條形碼和二維碼的編碼與解碼。
Zxing目前支持的格式如下:UPC-A、UPC-E、EAN-8、EAN-13、39碼、93碼、代碼128、QR碼。
Google一個開源的掃碼框架:zxing。
開源下載:http://code.google.com/p/zxing/
zxing是基於多種1D/2D條碼處理的開源庫,是一個完整的項目。它可以通過手機攝像頭實現條碼的掃描以及解碼,功能及其強大。那麼如果要實現二維碼的掃描以及解碼,我們需要在該開源項目的基礎上進行簡化,並修改。讓我們來看一下
上圖是仿照QQ的掃一掃進行修改的zxing項目,以zxing項目爲基礎,結合實際應用,這裏作了三點改變:
(1)豎屏掃描
(2)自定義取景框
(3)重新定義掃描結果處理
一、第一步:下載zxing項目,並簡化出掃描框架
1、首先,下載最新zxing開源項目。 下載地址:http://code.google.com/p/zxing/
2、分析項目結構,明確掃描框架需求。在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 基於常量存儲的常量類
3、新建工程,添加如下權限:
<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" />
添加core.jar文件,並BuildPath;將上述類或包加入工程後,會報一系列錯誤,原因有幾點:
(1)資源文件缺乏,將zxing下需要的資源文件copy到新工程下
(2)版本兼容問題,zxing下很多技術都是使用4.0版本及以上,集成到低版本之後,須做相應改動,詳情參照項目源碼
(3)包結構引用問題,需要重新導入包引用
4、簡化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佈局去掉結果顯示,添加標題欄。那麼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 }
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 }
接下來看如何初始化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 }
在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 }
5、將指定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 }
通過以上四步,zxing項目的簡化工作基本完成。至於一些類需要進行小修小改,希望可以對着源碼進行,這裏不再贅述。二維碼掃描的整體構架主要包含三部分:
1、定義取景框,也即掃描的View,通過surfaceview進行繪製
2、Camera, 掃描的核心在於camera的配置使用,包括預覽,自動聚焦,打開設備等處理
3、Decode解碼,掃描完成後整個工程的核心
除去以上三個模塊,需要明確的就是CaptureActivitiy中handleDeCode()方法做自己的處理。