zoukankan      html  css  js  c++  java
  • 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 

  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/exmyth/p/5142600.html
Copyright © 2011-2022 走看看