目前的很多应用上都有扫码功能,当时微信推出二维码扫码功能时,觉得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、新建工程,添加如下权限:
添加core.jar文件,并BuildPath;将上述类或包加入工程后,会报一系列错误,原因有几点:
(1)资源文件缺乏,将zxing下需要的资源文件copy到新工程下
(2)版本兼容问题,zxing下很多技术都是使用4.0版本及以上,集成到低版本之后,须做相应改动,详情参照项目源码
(3)包结构引用问题,需要重新导入包引用
4、简化CapyureActivity, camera包以及decode各类异常解决以后,即可对CaptureActivity进行代码简化 ,首先看一下capture.xml布局
13 4 5 9 10 11 15 16 17 22 23 39 4029 30 37 38
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()方法做自己的处理。