• Android自由行之走进zxing,轻松实现二维码扫描


         现在很多App都集成了扫一扫功能,最常用的微信、QQ、手机助手等。二维码也使得生活变得更加简洁,扫一扫订餐、扫一扫下载等等。那么,说到二维码,我们不得不提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" />
    permission

            添加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

            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

       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     }
    View Code

           通过以上四步,zxing项目的简化工作基本完成。至于一些类需要进行小修小改,希望可以对着源码进行,这里不再赘述。二维码扫描的整体构架主要包含三部分:

           1、定义取景框,也即扫描的View,通过surfaceview进行绘制

           2、Camera, 扫描的核心在于camera的配置使用,包括预览,自动聚焦,打开设备等处理

           3、Decode解码,扫描完成后整个工程的核心

           除去以上三个模块,需要明确的就是CaptureActivitiy中handleDeCode()方法做自己的处理。

           点击下面链接,即可下载源码

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

  • 相关阅读:
    JS事件处理中心的构想
    form的novalidate属性
    AOP思想在JS中的应用
    推行浏览器升级提示,从自己做起
    doT.js模板引擎
    关于JS获取元素宽度的一点儿思考
    类似百度图片,360图片页面的布局插件
    ASCII、Unicode、UTF-8编码关系
    python字符串格式化符号及转移字符含义
    python字符串的方法介绍
  • 原文地址:https://www.cnblogs.com/exmyth/p/5142600.html
Copyright © 2020-2023  润新知