Edited by mythou

原创博文,转载请标明出处:http://www.cnblogs.com/mythou/p/3280023.html

已测试 -- 绝对靠谱

今天讲一下目前移动领域很常用的技术——二维码。现在大街小巷、各大网站都有二维码的踪迹,不管是IOS、Android、WP都有相关支持的软件。之前我就想了解二维码是如何工作,最近因为工作需要使用相关技术,所以做了初步了解。今天主要是讲解如何使用ZXing库,生成和识别二维码。这篇文章实用性为主,理论性不会讲解太多,有兴趣可以自己查看源码。

1、ZXing库介绍

  这里简单介绍一下ZXing库。ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。Zxing可以实现使用手机的内置的摄像头完成条形码的扫描及解码。该项目可实现的条形码编码和解码。目前支持以下格式:UPC-A,UPC-E、EAN-8,EAN-13、39码、93码。ZXing是个很经典的条码/二维码识别的开源类库,以前在功能机上,就有开发者使用J2ME运用ZXing了,不过要支持JSR-234规范(自动对焦)的手机才能发挥其威力。

  下面是ZXing的demo运行,我这里创建了一个二维码,内容是我博客的网址,大伙可以用微信的扫一扫功能,试一下。就可以直接打开我博客。 2、ZXing库主要类

  下面给大家介绍一下,ZXing库里面主要的类以及这些类的作用:

  • CaptureActivity。这个是启动Activity 也就是扫描器。
  • CaptureActivityHandler 解码处理类,负责调用另外的线程进行解码。
  • DecodeThread 解码的线程。
  • com.google.zxing.client.android.camera 包,摄像头控制包。
  • ViewfinderView 自定义的View,就是我们看见的拍摄时中间的框框了。

3、使用ZXing生成二维码

  下面针对二维码生成和解析做个简单介绍,至于详细的使用方法,建议大家还是自己看看源码,使用起来很简单,不过这个开源项目的代码,值得好好看看。首先给出二维码生成的方法:

//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();        }    }
View Code

上面就是二维码生成的方法接口,如果你只是使用者方法,很简单,只要传入一个URL即可,就像我截图里面一样,传入一个合法的网址即可。或者像现在一些移动APP的推广,把APP下载地址转为二维码,只要扫一下就可以下载相应的APP。这个也是目前比较流行的APP的推广方式。

  上面代码做的事情不多,主要是调用ZXing库里面QRCodeWriter().encode的方法对我们传进去的URL进行编码,具体如何编码,这个我这里就不详细说,有兴趣可以看ZXing的源码。文章最后会给出ZXing的源码和例子代码。

4、扫描二维码获取信息

  扫描获取二维码信息的工作稍微复杂一些,主要是需要编写Camera的使用,这个跟我们一般使用Camera一样,需要使用Surfaceview作为预览,这一部我这里就不说了,这个应该不是太复杂。对于使用过Camera做预览的朋友,应该是挺简单的事情。获取二维码数据的关键处理是在Camera的自动对焦回调函数哪里,调用ZXing的解码接口。

//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();    }  }
View Code

这里稍微多说一句,由于解码需要一定时间,所以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;    }  }
View Code

  上面是解码的线程处理不同状态的时候需要注意的地方,我们这里只看获取图像成功的地方,成功获取图片解码的实在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();    }
View Code

上面就是整个二维码的解码流程,里面因为涉及很多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" />
View Code

5、结语

  上面就是生成和识别二维码的关键流程和代码,有兴趣的朋友可以自己查看ZXing的源码,里面有很多图像分析的知识可以学习。具体使用也可以参考我下面给出的Demo。二维码对于现在移动开发来说很是很常用的技术,所以有空可以了解一下,说不定什么时候就用上了。另外,ZXing库除了二维码外,其实对于条形码也是支持的,只是我这里没有介绍。有需要的自己去看看源码即可。

2013-8-16

Edited by 泡泡糖

ZXing开源项目Google Code地址:https://code.google.com/p/zxing/

ZXingDemo下载:ZXingDemo2013-8-25.rar

更多相关文章

  1. Android新特性之CardView的简单使用
  2. Android中获取网络图片的三种方法
  3. Android(安卓)源码导入Eclipse技巧(Ubuntu)
  4. [置顶] android利用zbar二维码扫描-(解决中文乱码及扫描区域定义
  5. Android(安卓)5.1 SurfaceFlinger VSYNC详解
  6. Android源码50例汇总,欢迎各位下载
  7. Android(安卓)Framework层看硬件加速
  8. android sdk, adt编译问题
  9. 【Android浏览器插件开发准备工作之NPAPI】使用Visual Studio 20

随机推荐

  1. Android按返回键,程序进入后台运行,不关闭
  2. 在线安装eclipse cdt的地址
  3. Android 文件的下载
  4. Android部分控件示例汇总
  5. Android Studio中 Error:Could not find
  6. Android Retrofit W/System.err: java.ne
  7. react native android 调试时图片没有显
  8. 使用ANT进行Android打包
  9. TextView使用Paint设置下划线删除线效果
  10. android读取系统相机及相册图片(亲测可用)