今天讲一下目前移动领域很经常使用的技术——二维码。如今大街小巷、各大网站都有二维码的踪影,不论是IOS、 Android、WP都有相关支持的软件。以前我就想了解二维码是如何工做,最近由于工做须要使用相关技术,因此作了初步了解。今天主要是讲解如何使用 ZXing库,生成和识别二维码。这篇文章实用性为主,理论性不会讲解太多,有兴趣能够本身查看源码。android
一、ZXing库介绍算法
这里简单介绍一下ZXing库。ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其余语言的端口。Zxing能够实现使用手机的内置的摄像头完成条形码的扫描及解码。该项目可实现的条形码编码和解码。目前支持如下格式:UPC-A,UPC-E、EAN-8,EAN-1三、39码、93码。ZXing是个很经典的条码/二维码识别的开源类库,之前在功能机上,就有开发者使用J2ME运用ZXing了,不过要支持JSR-234规范(自动对焦)的手机才能发挥其威力。浏览器
下面给你们介绍一下,ZXing库里面主要的类以及这些类的做用:微信
三、使用ZXing生成二维码ide
下面针对二维码生成和解析作个简单介绍,至于详细的使用方法,建议你们仍是本身看看源码,使用起来很简单,不过这个开源项目的代码,值得好好看看。首先给出二维码生成的方法:函数
//Edited by mythou //http://www.cnblogs.com/mythou/
//要转换的地址或字符串,能够是中文 public void createQRImage(String url) { try { //判断URL合法性 if (url == null || "".equals(url) || url.length() < 1) { return; } Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); //图像数据转换,使用了矩阵转换 BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints); int[] pixels = new int[QR_WIDTH * QR_HEIGHT]; //下面这里按照二维码的算法,逐个生成二维码的图片, //两个for循环是图片横列扫描的结果 for (int y = 0; y < QR_HEIGHT; y++) { for (int x = 0; x < QR_WIDTH; x++) { if (bitMatrix.get(x, y)) { pixels[y * QR_WIDTH + x] = 0xff000000; } else { pixels[y * QR_WIDTH + x] = 0xffffffff; } } } //生成二维码图片的格式,使用ARGB_8888 Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT); //显示到一个ImageView上面 sweepIV.setImageBitmap(bitmap); } catch (WriterException e) { e.printStackTrace(); } }
上面就是二维码生成的方法接口,若是你只是使用者方法,很简单,只要传入一个 URL便可,就像我截图里面同样,传入一个合法的网址便可。或者像如今一些移动APP的推广,把APP下载地址转为二维码,只要扫一下就能够下载相应的 APP。这个也是目前比较流行的APP的推广方式。学习
上面代码作的事情很少,主要是调用ZXing库里面QRCodeWriter().encode的方法对咱们传进去的URL进行编码,具体如何编码,这个我这里就不详细说,有兴趣能够看ZXing的源码。文章最后会给出ZXing的源码和例子代码。网站
四、扫描二维码获取信息ui
扫描获取二维码信息的工做稍微复杂一些,主要是须要编写Camera的使用,这个 跟咱们通常使用Camera同样,须要使用Surfaceview做为预览,这一部我这里就不说了,这个应该不是太复杂。对于使用过Camera作预览的 朋友,应该是挺简单的事情。获取二维码数据的关键处理是在Camera的自动对焦回调函数哪里,调用ZXing的解码接口。this
//Edited by mythou //http://www.cnblogs.com/mythou/
private void restartPreviewAndDecode() { if (state == State.SUCCESS) { state = State.PREVIEW; CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode); CameraManager.get().requestAutoFocus(this, R.id.auto_focus); activity.drawViewfinder(); } }
这里稍微多说一句,因为解码须要必定时间,因此ZXing的解码调用,都是使用了Handler做为线程通讯机制,解码的工做都是放在独立线程里面使用的,若是你直接在主线程解码,恐怕ANR问题是避免不了。
//Edited by mythou //http://www.cnblogs.com/mythou/
public void handleMessage(Message message) { switch (message.what) { case R.id.auto_focus: //Log.d(TAG, "Got auto-focus message"); // When one auto focus pass finishes, start another. This is the closest thing to // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do. if (state == State.PREVIEW) { CameraManager.get().requestAutoFocus(this, R.id.auto_focus); } break; case R.id.restart_preview: Log.d(TAG, "Got restart preview message"); restartPreviewAndDecode(); break; case R.id.decode_succeeded: //解码成功,获取到界面的结果和原来的二维码数据 Log.d(TAG, "Got decode succeeded message"); state = State.SUCCESS; Bundle bundle = message.getData(); Bitmap barcode = bundle == null ? null : (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP); activity.handleDecode((Result) message.obj, barcode); break; case R.id.decode_failed: // We're decoding as fast as possible, so when one decode fails, start another. state = State.PREVIEW; CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode); break; case R.id.return_scan_result: Log.d(TAG, "Got return scan result message"); activity.setResult(Activity.RESULT_OK, (Intent) message.obj); activity.finish(); break; case R.id.launch_product_query: Log.d(TAG, "Got product query message"); String url = (String) message.obj; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); activity.startActivity(intent); break; } }
上面是解码的线程处理不一样状态的时候须要注意的地方,咱们这里只看获取图像成功的 地方,成功获取图片解码的实在DecodeThread里面实现,DecodeThread里面解码成功后,会把数据序列化,而后保存到Bundle里 面,咱们能够直接经过Bundle的序列化,获取到图片数据。同时会把解码后的结果保存到MSG里面,而后就能够根据实际状况进行处理,例如上面代码,解 码成功后,会调用一个处理函数:
//Edited by mythou //http://www.cnblogs.com/mythou/
public void handleDecode(final Result obj, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); AlertDialog.Builder dialog = new AlertDialog.Builder(this); if (barcode == null) { dialog.setIcon(null); } else { Drawable drawable = new BitmapDrawable(barcode); dialog.setIcon(drawable); } dialog.setTitle("扫描结果"); dialog.setMessage(obj.getText()); dialog.setNegativeButton("肯定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //用默认浏览器打开扫描获得的地址 Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); Uri content_url = Uri.parse(obj.getText()); intent.setData(content_url); startActivity(intent); finish(); } }); dialog.setPositiveButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); dialog.create().show(); }
上面就是整个二维码的解码流程,里面由于涉及不少Camera的使用,因此你若是须要使用二维码识别,须要注意一下你的程序须要申请下面的权限,通常的Camera使用以及Camera的自动对焦等。
//Edited by mythou //http://www.cnblogs.com/mythou/
<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" />
五、结语
上面就是生成和识别二维码的关键流程和代码,有兴趣的朋友能够本身查看ZXing 的源码,里面有不少图像分析的知识能够学习。具体使用也能够参考我下面给出的Demo。二维码对于如今移动开发来讲非常很经常使用的技术,因此有空能够了解一 下,说不定何时就用上了。另外,ZXing库除了二维码外,其实对于条形码也是支持的,只是我这里没有介绍。有须要的本身去看看源码便可。
2013-8-16
Edited by 泡泡糖
ZXing开源项目Google Code地址:https://code.google.com/p/zxing/
ZXingDemo下载:ZXingDemo2013-8-25.rar