Android 基于google Zxing实现二维码、条形码扫描

首先我们看下项目结构

   

   (1) 首先我们从扫描二维码Activity MipcaActivityCapture.Java 类入手该类主要是调用相机预览拍照内容,处理扫描后的结果,扫码成功震动,及扫描音效等。

    首先我们看关键模块,相机拍摄预览用到为View控件SurfaceView 改控件提供了一个专用绘图面,嵌入在视图层次结构中。你可以控制整个表面的格式,它的大小;SurfaceView负责屏幕上正确的位置显示。

   SurfaceView提供了 SurfaceHolder接口来设置控件的表面大小和格式编辑表面像素等,SurfaceHolder提供了Android.view.SurfaceHolder.Callback 接口来处理SurfaceView显示,渲染,销毁等回调监听,下面看关键代码

  1. @Override  
  2.     protected void onResume() {  
  3.         super.onResume();  
  4.   
  5.         /** 
  6.          * 提供一个专用的绘图面,嵌入在视图层次结构中。你可以控制这个表面的格式,它的大小; 
  7.          * SurfaceView负责将面在屏幕上正确的位置显示。 
  8.          *  
  9.          * 表面是Z序是窗口举行SurfaceView落后;SurfaceView打出了一个洞在它的窗口,让其表面显示。 
  10.          * 视图层次结构将负责正确的合成与表面的任何兄弟SurfaceView通常会出现在它的上面 
  11.          * 。这可以用来放置覆盖如表面上的按钮,但注意,这可能会对性能产生影响因为完整的alpha混合复合材料将每一次表面的变化进行。 
  12.          *  
  13.          * 使表面可见的透明区域是基于视图层次结构中的布局位置的。如果布局后的变换属性用于在顶部的图形绘制一个兄弟视图,视图可能不正确的复合表面。 
  14.          *  
  15.          * 访问底层的表面通过SurfaceHolder接口提供,这可以通过调用getholder()检索。 
  16.          *  
  17.          * 表面将被创建为你而SurfaceView的窗口是可见的;你应该实现surfacecreated(SurfaceHolder) 
  18.          * 和surfacedestroyed(SurfaceHolder)发现当表面被创建和销毁窗口的显示和隐藏。 
  19.          *  
  20.          * 这个类的目的之一是提供一个表面,其中一个二级线程可以呈现到屏幕上。如果你要使用它,你需要知道一些线程的语义: 
  21.          *  
  22.          * 所有的图形和SurfaceHolder。回调方法将从线程运行的窗口叫SurfaceView(通常是应用程序的主线程)。因此, 
  23.          * 他们需要正确地与任何状态,也接触了绘图线程的同步。 
  24.          *  
  25.          * 你必须确保拉丝只触及表面,底层是有效的——SurfaceHolder.lockCanvas。回调。surfacecreated() 
  26.          * 和surfacedestroyed() SurfaceHolder。回调。 
  27.          */  
  28.         SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);  
  29.   
  30.         /** 
  31.          * SurfaceHolder 类解释 
  32.          *  
  33.          * 抽象接口,有人拿着一个显示面。允许你 
  34.          *  
  35.          * 控制的表面大小和格式,编辑在表面的像素,和 
  36.          *  
  37.          * *显示器更改为表面。此接口通常可用 
  38.          *  
  39.          * 通过SurfaceView类 {@link SurfaceView} 
  40.          *  
  41.          * 当使用该接口从一个线程以外的一个运行 {@link SurfaceView}, 你要仔细阅读 
  42.          *  
  43.          * 方法 {@link #lockCanvas} and {@link Callback#surfaceCreated 
  44.          * Callback.surfaceCreated()}. 
  45.          */  
  46.   
  47.         /** 
  48.          * surfaceView.getHolder() 返回SurfaceHolder 对象 
  49.          */  
  50.         SurfaceHolder surfaceHolder = surfaceView.getHolder();  
  51.           
  52.           
  53.         if (hasSurface) { // 判断是否 有显示  
  54.   
  55.             // 初始化相机  
  56.             initCamera(surfaceHolder);  
  57.         } else {  
  58.   
  59.             // 添加回调监听  
  60.             surfaceHolder.addCallback(this);  
  61.   
  62.             // 设置视图类型 这是被忽略的,这个值是在需要时自动设定的。  
  63.             surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  64.         }  
  65.   
  66.         // 解码格式  
  67.         decodeFormats = null;  
  68.   
  69.         // 字符集  
  70.         characterSet = null;  
  71.   
  72.         playBeep = true;  
  73.   
  74.         // 获取系统音频服务 AUDIO_SERVICE(音频服务)  
  75.         // AudioManager 提供了访问音量和振铃模式控制  
  76.         AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);  
  77.   
  78.         // 判断当前的模式 是否为 (铃声模式,可能是声音和振动。)  
  79.         if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {  
  80.   
  81.             // 设置 播放闹铃 为false  
  82.             playBeep = false;  
  83.         }  
  84.           
  85.         //* 初始化 报警音频  
  86.         initBeepSound();  
  87.   
  88.         // 设置震动状态为 true  
  89.         vibrate = true;  
  90.   
  91.     }  

    接下来看下初始化媒体播放器,及震动模块代码,MediaPlayer 做过流媒体或音频相关开发都用过,这里是用文件流加载

raw目录下的文件。 Vibrator类 操作该设备上的振子的类,也就是让我们手机产生震动效果,请看一下代码块,注释有很多是自己理解和百度翻译。

  1. /** 
  2.      * 初始化 报警音频 
  3.      */  
  4.     private void initBeepSound() {  
  5.         if (playBeep && mediaPlayer == null) {  
  6.   
  7.             // 在stream_system音量不可调的,用户发现它太大声,所以我们现在播放的音乐流。  
  8.             setVolumeControlStream(AudioManager.STREAM_MUSIC);  
  9.   
  10.             // 初始化 媒体播放器  
  11.             mediaPlayer = new MediaPlayer();  
  12.   
  13.             /* 
  14.              * 设置此播放器的音频流式。看到{@链接audiomanager } 
  15.              *  
  16.              * 对于一个流类型列表。必须调用这个方法之前,prepare() 或 
  17.              *  
  18.              * 为目标流式成为有效的为prepareasync() 
  19.              *  
  20.              * 此后。 
  21.              */  
  22.             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
  23.   
  24.             /** 
  25.              * 
  26.              当媒体源的结束时调用一个回调函数 已达到在播放。 
  27.              * 
  28.              * @param监听器回调将运行 
  29.              */  
  30.             mediaPlayer.setOnCompletionListener(beepListener);  
  31.   
  32.             /** 
  33.              * 在资源管理入口文件描述符。这提供你自己的 
  34.              *  
  35.              * 打开FileDescriptor,可以用来读取数据,以及 
  36.              *  
  37.              * 该项数据在文件中的偏移和长度。 
  38.              */  
  39.             AssetFileDescriptor file = getResources().openRawResourceFd(  
  40.                     R.raw.beep);  
  41.             try {  
  42.   
  43.                 /** 
  44.                  * file.getFileDescriptor() 返回FileDescriptor,可以用来读取的数据文件。 
  45.                  * 
  46.                  * setDataSource() 设置数据源(FileDescriptor)使用。这是来电者的责任 
  47.                  *  
  48.                  * 关闭文件描述符。这是安全的,这样做,只要这个呼叫返回。 
  49.                  */  
  50.                 mediaPlayer.setDataSource(file.getFileDescriptor(),  
  51.                         file.getStartOffset(), file.getLength());  
  52.   
  53.                 // 关闭 资源文件管理器  
  54.                 file.close();  
  55.   
  56.                 /** 
  57.                  * 设置该播放器的音量。 
  58.                  *  
  59.                  * 此接口建议用于平衡音频流的输出 
  60.                  *  
  61.                  * 在一个应用程序中。除非你正在写一个申请 
  62.                  *  
  63.                  * 控制用户设置时,应优先使用该原料药 
  64.                  * 
  65.                  * {@link AudioManager#setStreamVolume(int, int, int)} 
  66.                  * 其中设置的所有流的体积 
  67.                  *  
  68.                  * 特定类型。请注意,通过量值是在范围0到1原标量。 
  69.                  *  
  70.                  * UI控件应该相应的对数。 
  71.                  *  
  72.                  * @param leftVolume 
  73.                  *  
  74.                  *            左量标量 
  75.                  *  
  76.                  * @param rightVolume 
  77.                  *            对体积标量 
  78.                  */  
  79.                 mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);  
  80.   
  81.                 /** 
  82.                  * 准备播放,同步播放。 
  83.                  *  
  84.                  * 在设置数据源和显示表面,你要么 
  85.                  *  
  86.                  * 电话prepare()或prepareasync()。文件,就可以prepare(), 
  87.                  *  
  88.                  * 块直到MediaPlayer准备播放。 
  89.                  *  
  90.                  * @throws IllegalStateException 
  91.                  *             如果被称为无效状态 
  92.                  */  
  93.                 mediaPlayer.prepare();  
  94.             } catch (IOException e) {  
  95.                 mediaPlayer = null// 异常 释放播放器对象  
  96.             }  
  97.         }  
  98.     }  
  99.   
  100.     // 震动持续时间  
  101.     private static final long VIBRATE_DURATION = 200L;  
  102.   
  103.     /** 
  104.      * 打声音和振动 
  105.      */  
  106.     private void playBeepSoundAndVibrate() {  
  107.         if (playBeep && mediaPlayer != null) {  
  108.   
  109.             /** 
  110.              * 开始或恢复播放。如果播放以前被暂停, 
  111.              *  
  112.              * 播放将继续从它被暂停的地方。如果播放了 
  113.              *  
  114.              * 被停止,或从未开始,播放将开始在 
  115.              *  
  116.              * 开始。 
  117.              * 
  118.              * @throws IllegalStateException 
  119.              *             如果被称为无效状态 
  120.              */  
  121.             mediaPlayer.start();  
  122.         }  
  123.         if (vibrate) {  
  124.   
  125.             /** 
  126.              * getSystemService(VIBRATOR_SERVICE); 
  127.              *  
  128.              * 使用 {@link #getSystemService}检索{@link android.os.Vibrator} 
  129.              * 与振动硬件相互作用。 
  130.              * 
  131.              * @see #getSystemService 
  132.              * @see android.os.Vibrator 
  133.              *  
  134.              *  
  135.              *      Vibrator类 操作该设备上的振子的类。 如果你的进程存在,你开始的任何振动都将停止。 
  136.              *  
  137.              *      要获得系统振子的实例,调用 {@link Context#getSystemService}具有 
  138.              *      {@link Context#VIBRATOR_SERVICE} 作为参数。 
  139.              */  
  140.             Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);  
  141.   
  142.             /** 
  143.              * 为指定的时间周期振动。 
  144.              *  
  145.              * 此方法要求调用方持有权限 
  146.              *  
  147.              * {@link android.Manifest.permission#VIBRATE}. 
  148.              *  
  149.              * @param milliseconds 
  150.              *            振动的毫秒数。 
  151.              *  
  152.              *            VIBRATE_DURATION 震动持续时间 
  153.              */  
  154.             vibrator.vibrate(VIBRATE_DURATION);  
  155.         }  
  156.     }  
  157.   
  158.     /** 
  159.      * 在播放时调用一个回调函数的接口定义媒体来源已完成 
  160.      */  
  161.     private final OnCompletionListener beepListener = new OnCompletionListener() {  
  162.         public void onCompletion(MediaPlayer mediaPlayer) {  
  163.   
  164.             /** 
  165.              * 寻找指定的时间位置。 
  166.              * 
  167.              * @param 毫秒毫秒的偏移从开始寻求 
  168.              *  
  169.              * @抛出时,如果内部播放器引擎尚未初始化 
  170.              */  
  171.             mediaPlayer.seekTo(0);  
  172.         }  
  173.     };  

    接下来看相机初始化模块,及相机控制模块,这里用到了Activity生命周期函数,主要是关闭相机,终止线程等相关操作。

  1. /** 
  2.      * 初始化 
  3.      *  
  4.      * @param surfaceHolder 
  5.      */  
  6.     private void initCamera(SurfaceHolder surfaceHolder) {  
  7.         try {  
  8.   
  9.             // 打开摄像头驱动和初始化硬件参数。  
  10.             CameraManager.get().openDriver(surfaceHolder);  
  11.   
  12.         } catch (IOException ioe) {  
  13.             return;  
  14.         } catch (RuntimeException e) {  
  15.             return;  
  16.         }  
  17.   
  18.         if (handler == null) {  
  19.   
  20.             // 这个类处理所有的消息,包括为捕获的异常  
  21.             handler = new CaptureActivityHandler(this, decodeFormats,  
  22.                     characterSet);  
  23.         }  
  24.     }  

  1. /** 
  2.  * 当 Activity 失去焦点时调用 
  3.  */  
  4. @Override  
  5. protected void onPause() {  
  6.     super.onPause();  
  7.     if (handler != null) {  
  8.   
  9.         // 退出同步  
  10.         handler.quitSynchronously();  
  11.   
  12.         handler = null;  
  13.     }  
  14.   
  15.     // 关闭摄像头驱动程序,如果仍在使用  
  16.     CameraManager.get().closeDriver();  
  17. }  
  18.   
  19. /** 
  20.  * 销毁时调用 
  21.  */  
  22. @Override  
  23. protected void onDestroy() {  
  24.   
  25.     /** 
  26.      * 启动一个有序的关机之前提交的 
  27.      *  
  28.      * 任务执行,但没有新任务将被接受。 
  29.      *  
  30.      * 如果已关闭,没有任何附加效果 
  31.      */  
  32.     inactivityTimer.shutdown();  
  33.   
  34.     super.onDestroy();  
  35. }  
    最后我们来看看,如何处理扫描结果的,这里用到了 CaptureActivityHandler 这个类继承 Handler,类中封装了解码线程类DecodeThread 这里我们先看 当前扫描Activity如何处理扫描后处理结果的函数 public void handleDecode(Result result, Bitmap barcode) ;这个函数主要是处理扫描成功后效果,拿到扫描后回传结果等

  1. /** 
  2.      * 处理扫描结果 
  3.      *  
  4.      * @param result 
  5.      * @param barcode 
  6.      */  
  7.     public void handleDecode(Result result, Bitmap barcode) {  
  8.   
  9.         // 试图取消此任务的执行 , 创建并执行将启用的一一个射击动作在给定的延迟。  
  10.         inactivityTimer.onActivity();  
  11.   
  12.         // 打声音和振动  
  13.         playBeepSoundAndVibrate();  
  14.   
  15.         // 获取扫描结果  
  16.         String resultString = result.getText();  
  17.   
  18.         if (resultString.equals("")) {  
  19.   
  20.             Toast.makeText(MipcaActivityCapture.this"Scan failed!",  
  21.                     Toast.LENGTH_SHORT).show();  
  22.   
  23.         } else {  
  24.   
  25.             // 回传扫描结果  
  26.             Intent resultIntent = new Intent();  
  27.             Bundle bundle = new Bundle();  
  28.             bundle.putString("result", resultString);  
  29.             bundle.putParcelable("bitmap", barcode);  
  30.             resultIntent.putExtras(bundle);  
  31.             this.setResult(RESULT_OK, resultIntent);  
  32.         }  
  33.   
  34.         MipcaActivityCapture.this.finish();  
  35.     }  

   (2)以上只是浅谈如何调用相机,以及处理扫描效果等,接线来深入分析扫描线程,及处理扫描效果 handler 回调等,刚才有讲到CaptureActivityHandler 类这个类处理Handler消息下面就看相关代码。
   首先我们看下这个类的构造函数,在这个构造函数中,构造了解码线程类 DecodeThread类,这个类非常关键稍后会讲到,这里要注意,我们在构建中已经启用线程     decodeThread.start();

  1. /** 
  2.  *  
  3.  * @param activity  处理 Handler消息的Activity 
  4.  * @param decodeFormats 条形码格式结合 
  5.  * @param characterSet   字符集 
  6.  */  
  7. public CaptureActivityHandler(MipcaActivityCapture activity,  
  8.         Vector<BarcodeFormat> decodeFormats, String characterSet) {  
  9.       
  10.     this.activity = activity;  
  11.       
  12.     decodeThread = new DecodeThread(activity, decodeFormats, characterSet,  
  13.             new ViewfinderResultPointCallback(activity.getViewfinderView()));  
  14.       
  15.     decodeThread.start();  
  16.     state = State.SUCCESS;  
  17.   
  18.     //开始自己捕捉预览解码。  
  19.     CameraManager.get().startPreview();  
  20.   
  21.     restartPreviewAndDecode();  
  22. }  

   看到了构造处理消息 Handler类代码块,那么还记得那个模块构造的该类对象不,我们刚才有讲到相机初始化模块请看一下代码。

  1. /** 
  2.  * 初始化 
  3.  *  
  4.  * @param surfaceHolder 
  5.  */  
  6. private void initCamera(SurfaceHolder surfaceHolder) {  
  7.     try {  
  8.   
  9.         // 打开摄像头驱动和初始化硬件参数。  
  10.         CameraManager.get().openDriver(surfaceHolder);  
  11.   
  12.     } catch (IOException ioe) {  
  13.         return;  
  14.     } catch (RuntimeException e) {  
  15.         return;  
  16.     }  
  17.   
  18.     if (handler == null) {  
  19.   
  20.         // 这个类处理所有的消息,包括为捕获的异常  
  21.         handler = new CaptureActivityHandler(this, decodeFormats,  
  22.                 characterSet);  
  23.     }  
  24. }  

   接下来分析这个类的关键模块 Handler消息处理模块 handleMessage()消息处理函数,这里肯定大家会对一些用到的Id感到好奇其实这里的Id是定义在 下图中xml文件中,大家可以详细看下。


   接下来言归正传,还是继续分析 handleMessage(),这里有用到枚举类,用来标记状态,public void handleMessage(Message message)函数都有注释,这里就不详解了。

  1. private enum State {  
  2.         PREVIEW, // 预览  
  3.         SUCCESS, // 成功  
  4.         DONE // 完成  
  5.     }  

  1. @Override  
  2. public void handleMessage(Message message) {  
  3.     switch (message.what) {  
  4.     case R.id.auto_focus:  
  5.         // Log.d(TAG, "Got auto-focus message");  
  6.   
  7.         /** 
  8.          * 当一个自动对焦结束,开始另一个。这是 
  9.          *  
  10.          * 最接近的 
  11.          *  
  12.          * 连续AF似乎找了一点,但我不确定是什么 
  13.          *  
  14.          * 做其他的。 
  15.          */  
  16.         if (state == State.PREVIEW) {  
  17.             CameraManager.get().requestAutoFocus(this, R.id.auto_focus);  
  18.         }  
  19.   
  20.         break;  
  21.     case R.id.restart_preview:  
  22.         Log.d(TAG, "Got restart preview message");  
  23.           
  24.         //重新启动预览和解码  
  25.         restartPreviewAndDecode();  
  26.         break;  
  27.     case R.id.decode_succeeded:  //得到解码成功消息  
  28.           
  29.         Log.d(TAG, "Got decode succeeded message");  
  30.         state = State.SUCCESS;  
  31.         Bundle bundle = message.getData();  
  32.   
  33.         /***********************************************************************/  
  34.         Bitmap barcode = bundle == null ? null : (Bitmap) bundle  
  35.                 .getParcelable(DecodeThread.BARCODE_BITMAP);  
  36.   
  37.         activity.handleDecode((Result) message.obj, barcode);  
  38.   
  39.         break;  
  40.     case R.id.decode_failed:  
  41.         /** 
  42.          * 我们尽可能快地解码,所以当一个解码失败,开始另一个。 
  43.          */  
  44.         state = State.PREVIEW;  
  45.         CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),  
  46.                 R.id.decode);  
  47.         break;  
  48.     case R.id.return_scan_result:  
  49.   
  50.         Log.d(TAG, "返回扫描结果消息");  
  51.         activity.setResult(Activity.RESULT_OK, (Intent) message.obj);  
  52.         activity.finish();  
  53.         break;  
  54.     case R.id.launch_product_query:  
  55.   
  56.         Log.d(TAG, "产品查询消息");  
  57.         String url = (String) message.obj;  
  58.         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));  
  59.         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);  
  60.         activity.startActivity(intent);  
  61.         break;  
  62.     }  
  63. }  

   下面看下解码过程中,用到了两个关键函数 quitSynchronously()该函数主要是处理相机关闭相机预览帧,阻止线程,清空Handler消息队列。细心的同学会发现该函数是在 处理扫描的Activity 生命周期函数 onPause()函数中用到

  1. /** 
  2.      * 退出同步 
  3.      */  
  4.     public void quitSynchronously() {  
  5.         state = State.DONE;  
  6.   
  7.         // 告诉相机停止绘制预览帧。  
  8.         CameraManager.get().stopPreview();  
  9.   
  10.         Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);  
  11.   
  12.         /** 
  13.          * 
  14.          * 将此消息发送到指定的处理程序 {@link #getTarget}. 如果该字段未被设置,则会引发一个空指针异常。 
  15.          */  
  16.         quit.sendToTarget();  
  17.         try {  
  18.   
  19.             /** 
  20.              * 阻止当前线程 <code>Thread.currentThread()</code>) 接收器完成它的执行和死亡。 
  21.              *  
  22.              * @throws InterruptedException 
  23.              *  如果当前线程被中断。 当前线程的中断状态将在异常之前被清除 
  24.              *  
  25.              * @see Object#notifyAll 
  26.              * @see java.lang.ThreadDeath 
  27.              */  
  28.             decodeThread.join();  
  29.         } catch (InterruptedException e) {  
  30.             // continue  
  31.         }  
  32.   
  33.         // 绝对肯定我们不会发送任何排队的消息  
  34.         removeMessages(R.id.decode_succeeded);  
  35.         removeMessages(R.id.decode_failed);  
  36.     }  

   下面还有一个关键模块,主要是处理,重新启动预览和解码函数 restartPreviewAndDecode() 这里有用到 CameraManager类 该类是相机管理类稍后会讲到

  1. /** 
  2.  * 重新启动预览和解码 
  3.  */  
  4. private void restartPreviewAndDecode() {  
  5.     if (state == State.SUCCESS) {  
  6.         state = State.PREVIEW;  
  7.         CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),  
  8.                 R.id.decode);  
  9.         CameraManager.get().requestAutoFocus(this, R.id.auto_focus);  
  10.         activity.drawViewfinder();  
  11.     }  
  12. }  


   (3)讲完了Handler消息回传可能大家还是不明白如何加码过程,接下来深入分析解码线程类 DecodeThread类,首先我们来看下这个类的构造函数,这里用到了 CountDownLatch 类这个类可能大家也不常用,我也是第一次接触,这里可以参考博客

http://blog.csdn.NET/shihuacai/article/details/8856370 讲的很详细,

CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

构造函数中用到 Vector<BarcodeFormat> decodeFormats 集合,该集合主要是封装了解码格式,用到了DecodeFormatManager 解码格式管理类,稍后会讲到改类。

  1. /** 
  2.      *  
  3.      * @param activity 
  4.      * @param decodeFormats     条形码格式 
  5.      * @param characterSet      字符集 
  6.      * @param resultPointCallback  结果回调接口 
  7.      */  
  8.     DecodeThread(MipcaActivityCapture activity,  
  9.             Vector<BarcodeFormat> decodeFormats, String characterSet,  
  10.             ResultPointCallback resultPointCallback) {  
  11.   
  12.         this.activity = activity;  
  13.           
  14.         /** 
  15.          * 构建了一个countdownlatch与给定的计数初始化。 
  16.          *  
  17.          * * @param count 次数 {@link #countDown}  必须调用 
  18.          *  
  19.          * 在线程可以通过 {@link #await} 
  20.          *  
  21.          *  @throws IllegalArgumentException  如果 {@code count}  是负数引发异常 
  22.          */  
  23.         handlerInitLatch = new CountDownLatch(1);  
  24.   
  25.         hints = new Hashtable<DecodeHintType, Object>(3);  
  26.   
  27.         //DecodeFormatManager类   这里把之前添加好的几个常量类,添加到解码的方法里面去,这样解码方法里面就有了所有的解码格式了,包括一维码和二维码。  
  28.         if (decodeFormats == null || decodeFormats.isEmpty()) {  
  29.             decodeFormats = new Vector<BarcodeFormat>();  
  30.             decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);  
  31.             decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);  
  32.             decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);  
  33.         }  
  34.   
  35.         /** 
  36.          * DecodeHintType 解码提示类型 
  37.          *  
  38.          * DecodeHintType.POSSIBLE_FORMATS 枚举值 
  39.          */  
  40.         hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);  
  41.   
  42.         if (characterSet != null) {  
  43.             hints.put(DecodeHintType.CHARACTER_SET, characterSet);  
  44.         }  
  45.   
  46.         hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK,  
  47.                 resultPointCallback);  
  48.     }  


   下面我们就看下线程类最关键模块线程体,这里是在线程中构造 DecodeHandler 类Handler ,下面还有一个函数处理Handler


  1. @Override  
  2. public void run() {  
  3.     /** 
  4.      * 初始化当前线程作为一个活套。 
  5.      *  
  6.      * 这给了你一个机会来创建处理程序,然后引用 
  7.      *  
  8.      * 这活套,然后再开始循环。一定要打电话 
  9.      *  
  10.      * {@link #loop()} 调用此方法后,通过调用 {@link #quit()}. 
  11.      *  
  12.      * 如果当前计数等于零,则没有发生任何事情。 
  13.      */  
  14.     Looper.prepare();  
  15.   
  16.     handler = new DecodeHandler(activity, hints);  
  17.   
  18.     /** 
  19.      * decrements伯爵的闩锁释放所有等待的线程,如果在达到零计数。 
  20.      *  
  21.      * 如果当前计数大于零则递减。 
  22.      *  
  23.      * 如果新计数为零,则所有等待线程被重新启用 
  24.      *  
  25.      * 线程调度的目的。 
  26.      * 
  27.      * 如果当前计数等于零,则没有发生任何事情。 
  28.      */  
  29.     handlerInitLatch.countDown();  
  30.   
  31.     /** 
  32.      * 调用此方法后,通过调用 
  33.      *  
  34.      * {@link #quit()} 结束循环。 
  35.      */  
  36.     Looper.loop();  
  37. }  

  1. Handler getHandler() {  
  2.     try {  
  3.   
  4.         /** 
  5.          * 导致当前线程等待锁存已计数的零,除非该线程是 {@linkplain Thread#interrupt interrupted}. 
  6.          *  
  7.          *  
  8.          *  
  9.          * 如果当前计数为零,则此方法立即返回。 如果当前计数大于零,则电流 
  10.          *  
  11.          * 线程成为禁用线程调度的目的和谎言休眠,直到有两点发生: 
  12.          *  
  13.          * 计数达到零由于调用的 {@link #countDown} 法;或 其他线程 { @linkplain 
  14.          * Thread#interrupt interrupts} 
  15.          * 当前线程。 
  16.          */  
  17.         handlerInitLatch.await();  
  18.     } catch (InterruptedException ie) {  
  19.         // continue?  
  20.     }  
  21.     return handler;  
  22. }  


   (4)这些Handler和线程在哪里发挥它的价值呢,接下来请看 CameraManager 相机管理类,在CaptureActivityHandler 构造类中有提到CameraManager类的函数调用,接下了深入了解这个类,这个类被封装成了单例类,那么在哪里初始化的呢,请看代码 CameraManager.init(getApplication()); 这行代码肯定很熟悉,在扫描二维码Activity的 onCreate()函数中出现过。 CameraManager 的get()函数提供了当前类的对象。

    构造函数中提供了三个类 CameraConfigurationManager相机配置管理器类和 AutoFocusCallback 类回调接口用来通知自动对焦完成,PreviewCallback类 用于提供预览帧的副本的回调接口,稍后会讲到这些类。

  1. /** 
  2.      * 随着调用活动的上下文初始化静态对象。 
  3.      * 
  4.      * @param context 
  5.      *            The Activity which wants to use the camera. 
  6.      */  
  7.     public static void init(Context context) {  
  8.         if (cameraManager == null) {  
  9.             cameraManager = new CameraManager(context);  
  10.         }  
  11.     }  
  12.   
  13.     /** 
  14.      * 得到cameramanager singleton实例。 
  15.      * 
  16.      * @return 返回一个参考的cameramanager 单例 
  17.      */  
  18.     public static CameraManager get() {  
  19.         return cameraManager;  
  20.     }  
  21.   
  22.     private CameraManager(Context context) {  
  23.   
  24.         this.context = context;  
  25.         this.configManager = new CameraConfigurationManager(context);  
  26.   
  27.         // 摄像机。setoneshotpreviewcallback() 在 Android (蛋糕版本)的竞争条件,所以我们使用旧的  
  28.         // 摄像机。setpreviewcallback() 1.5和更早。 在 Android (甜甜圈)和后,我们需要使用  
  29.         // 一次打回的球越打越高,因为年纪越大,就可以淹没系统,导致它  
  30.         // 从内存中耗尽。我们不能用sdk_int由于引入的Donut SDK。  
  31.         // useoneshotpreviewcallback =整数。parseInt(版本。版本。SDK)>  
  32.         // build.version_codes.cupcake;  
  33.         useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3// 3  
  34.                                                                                 // =  
  35.                                                                                 // Cupcake  
  36.   
  37.         previewCallback = new PreviewCallback(configManager,  
  38.                 useOneShotPreviewCallback);  
  39.         autoFocusCallback = new AutoFocusCallback();  
  40.     }  

   接下来我们就看下如何打开相机的,SurfaceHolder这个接口肯定不陌生,这个函数在相机初始化函数中有调用

 initCamera(SurfaceHolder surfaceHolder),其实相机说拍摄的到的东西都是在SurfaceView上呈现在你眼前的,这里对 SurfaceView最关键的操作类SurfaceHolder 。这里有用到 CameraConfigurationManager相机配置管理类对象,稍后会讲到。

  1. /** 
  2.  * 打开摄像头驱动和初始化硬件参数。 
  3.  * 
  4.  * @param holder 
  5.  *            相机将绘制预览帧的表面对象。 
  6.  *  
  7.  * @throws IOException 
  8.  *             异常表示相机驱动程序未能打开。 
  9.  *  
  10.  */  
  11. public void openDriver(SurfaceHolder holder) throws IOException {  
  12.     if (camera == null) {  
  13.         camera = Camera.open();  
  14.         if (camera == null) {  
  15.             throw new IOException();  
  16.         }  
  17.   
  18.         /** 
  19.          * *设置用于实时预览的 {@link Surface} 
  20.          *  
  21.          * *表面或表面纹理是必要的预览,和 
  22.          *  
  23.          * 预览是必要的拍照。相同的表面可以重新设定 
  24.          *  
  25.          * 没有伤害。设置一个预览面将不设置任何预览表面 
  26.          *  
  27.          * 纹理是通过 {@link #setPreviewTexture}.。 
  28.          *  
  29.          * 
  30.          *  
  31.          * <P> 
  32.          * 的{@link #setPreviewTexture必须已经包含一个表面时,这 
  33.          *  
  34.          * 方法被称为。如果你使用的是Android {@link android.view.SurfaceView}, 
  35.          *  
  36.          * 你需要登记一个{@link android.view.SurfaceView},用。 
  37.          *  
  38.          * {@link SurfaceHolder#addCallback(SurfaceHolder.Callback)} 和 
  39.          *  
  40.          * {@link SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)} 之前 
  41.          *  
  42.          * 通知 setpreviewdisplay()或启动预览。 
  43.          *  
  44.          * <P> 
  45.          * 方法必须调用之前{@link #startPreview()}. 。这个 
  46.          *  
  47.          * 一个例外是,如果预览表面没有设置(或设置为空) 
  48.          *  
  49.          * 在startpreview()叫,那么这种方法可以调用一次 
  50.          *  
  51.          * 与非空参数设置预览表面。(这让 
  52.          *  
  53.          * 相机设置和表面创建发生在平行,节省时间。) 
  54.          *  
  55.          * 预览版在运行时可能没有其他更改。 
  56.          *  
  57.          * 
  58.          *  
  59.          * @param夹含表面上放置预览, 
  60.          *  
  61.          *                  或空删除预览表面 
  62.          *  
  63.          * @抛出IOException如果方法失败(例如,如果表面 
  64.          *  
  65.          *                              不可用或不适合)。 
  66.          */  
  67.         camera.setPreviewDisplay(holder);  
  68.   
  69.         if (!initialized) {  
  70.             initialized = true;  
  71.   
  72.             configManager.initFromCameraParameters(camera);  
  73.         }  
  74.         configManager.setDesiredCameraParameters(camera);  
  75.   
  76.         // FIXME  
  77.         // SharedPreferences prefs =  
  78.         // PreferenceManager.getDefaultSharedPreferences(context);  
  79.         // 是否使用前灯  
  80.         // if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false))  
  81.         // {  
  82.         // FlashlightManager.enableFlashlight();  
  83.         // }  
  84.         FlashlightManager.enableFlashlight();  
  85.     }  
  86. }  
  1. /** 
  2.      * 关闭摄像头驱动程序,如果仍在使用 
  3.      */  
  4.     public void closeDriver() {  
  5.         if (camera != null) {  
  6.             FlashlightManager.disableFlashlight();  
  7.             camera.release();  
  8.             camera = null;  
  9.         }  
  10.     }  


   接下来看相机的启用和关闭,这里主要是对相机进行操作,相机的绘制预览帧,及监听等
  1. /** 
  2.      * 关闭摄像头驱动程序,如果仍在使用 
  3.      */  
  4.     public void closeDriver() {  
  5.         if (camera != null) {  
  6.             FlashlightManager.disableFlashlight();  
  7.             camera.release();  
  8.             camera = null;  
  9.         }  
  10.     }  
  11.   
  12.     /** 
  13.      * 要求摄像机硬件开始绘制预览帧到屏幕上。 
  14.      */  
  15.     public void startPreview() {  
  16.         if (camera != null && !previewing) {  
  17.   
  18.             /** 
  19.              * 开始捕获并绘制预览帧到屏幕。 
  20.              *  
  21.              * 预览将不会真正开始,直到提供一个表面 
  22.              * 
  23.              * {@link #setPreviewDisplay(SurfaceHolder)} or 
  24.              * {@link #setPreviewTexture(SurfaceTexture)}. 
  25.              * 
  26.              * 
  27.              * 如果 {@link #setPreviewCallback(Camera.PreviewCallback)}, 
  28.              * {@link #setOneShotPreviewCallback(Camera.PreviewCallback)}, or 
  29.              * {@link #setPreviewCallbackWithBuffer(Camera.PreviewCallback)} 
  30.              * 
  31.              * 是称{@link Camera.PreviewCallback#onPreviewFrame(byte[], Camera)} 
  32.              * 
  33.              ** 预览数据将成为可用。 
  34.              */  
  35.             camera.startPreview();  
  36.             previewing = true;  
  37.         }  
  38.     }  
  39.   
  40.     /** 
  41.      * 告诉相机停止绘制预览帧。 
  42.      */  
  43.     public void stopPreview() {  
  44.         if (camera != null && previewing) {  
  45.             if (!useOneShotPreviewCallback) {  
  46.   
  47.                 /** 
  48.                  * 除此之外,还安装了一个回调函数来调用每个预览帧 
  49.                  *  
  50.                  * 在屏幕上显示。这个回调将被反复调用 
  51.                  *  
  52.                  * 只要预览是活动的。这种方法可以随时调用, 
  53.                  *  
  54.                  * 即使预览是活的。其他预览回调 
  55.                  *  
  56.                  * 重写。 
  57.                  * 
  58.                  * 如果你使用的是预览数据来创建视频或静止图像, 
  59.                  *  
  60.                  * 强烈考虑使用 {@link android.media.MediaActionSound} 
  61.                  * 
  62.                  * 到 
  63.                  *  
  64.                  * 正确地显示图像捕捉或记录开始/停止给用户 
  65.                  */  
  66.                 camera.setPreviewCallback(null);  
  67.             }  
  68.             camera.stopPreview();  
  69.   
  70.             previewCallback.setHandler(null0);  
  71.             autoFocusCallback.setHandler(null0);  
  72.             previewing = false;  
  73.         }  
  74.     }  

   下面看相机,执行对焦等相关函数 requestPreviewFrame() 一个单独的预览框将返回给处理程序提供的处理。在CaptureActivityHandler类的handleMessage()函数和 restartPreviewAndDecode()函数中有调用,用户解码失败后的重新对焦,和重新启动预览和解码时有调用。

requestAutoFocus()请求相机的硬件来执行自动对焦。与requestPreviewFrame()出现的位置同样有调用。

   这里有讲到两个重要的监听类 PreviewCallback类:用于提供预览帧的副本的回调接口,AutoFocusCallback类: 回调接口用来通知自动对焦完成,这两个类是相机回调监听接口,提供了设置Handler和,回调函数。requestPreviewFramerequestPreviewFramerequestPreviewFramerequestPreviewFrame


  1. /** 
  2.  * 一个单独的预览框将返回给处理程序提供的处理。数据将作为字节到达 
  3.  * 在message.obj场,宽度和高度编码为message.arg1和message.arg2, 分别。 
  4.  * 
  5.  * @param handler 
  6.  *            发送消息的处理程序 
  7.  *  
  8.  * @param message 
  9.  *            要发送的消息的字段。 
  10.  *  
  11.  */  
  12. public void requestPreviewFrame(Handler handler, int message) {  
  13.     if (camera != null && previewing) {  
  14.         previewCallback.setHandler(handler, message);  
  15.         if (useOneShotPreviewCallback) {  
  16.   
  17.             /** 
  18.              * 安装在下一个预览帧中调用的回调函数 
  19.              *  
  20.              * 除了在屏幕上显示。一次调用之后 
  21.              *  
  22.              * 回调被清除。这种方法可以称为任何时间,甚至当 
  23.              *  
  24.              * 预览是活的。其他预览回调重写 
  25.              *  
  26.              * 如果你使用的是预览数据来创建视频或静止图像, 
  27.              *  
  28.              * 强烈考虑使用 {@link android.media.MediaActionSound} 
  29.              * 
  30.              * 正确地显示图像捕捉或记录开始/停止给用户。 
  31.              */  
  32.             camera.setOneShotPreviewCallback(previewCallback);  
  33.         } else {  
  34.   
  35.             /** 
  36.              * 安装一个回调以供每个预览帧调用 
  37.              *  
  38.              * 在屏幕上显示。这个回调将被反复调用 
  39.              *  
  40.              * 只要预览是活动的。这种方法可以随时调用, 
  41.              *  
  42.              * 即使预览是活的。其他预览回调 
  43.              *  
  44.              * 重写。 
  45.              * 
  46.              * 如果你使用的是预览数据来创建视频或静止图像, 
  47.              *  
  48.              * 强烈考虑使用 {@link android.media.MediaActionSound} 
  49.              * 
  50.              * 正确地显示图像捕捉或记录开始/停止给用户 
  51.              * 
  52.              ** @param 可接收每个预览帧的副本的回调对象 
  53.              *            , 
  54.              *  
  55.              * @see看 android.media.MediaActionSound 
  56.              * 
  57.              */  
  58.             camera.setPreviewCallback(previewCallback);  
  59.         }  
  60.     }  
  61. }  
  62.   
  63. /** 
  64.  * 请求相机的硬件来执行自动对焦。 
  65.  * 
  66.  * @param处理器处理通知时,自动对焦完成。 
  67.  * @param消息的消息传递。 
  68.  */  
  69. public void requestAutoFocus(Handler handler, int message) {  
  70.     if (camera != null && previewing) {  
  71.         autoFocusCallback.setHandler(handler, message);  
  72.         // Log.d(TAG, "Requesting auto-focus callback");  
  73.   
  74.         /** 
  75.          * 启动相机自动对焦,并注册一个回调函数来运行 
  76.          *  
  77.          * 相机聚焦。此方法仅在预览时有效 
  78.          * 
  79.          * (之间 {@link #startPreview()} and before {@link #stopPreview()}) 
  80.          *  
  81.          * 来电者应检查 {@link android.hardware.Camera.Parameters#getFocusMode()} 
  82.          *  
  83.          * 这种方法应该被称为。如果摄像头不支持自动对焦, 
  84.          *  
  85.          * 这是一个没有OP和 {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} 
  86.          * 
  87.          * 回调将立即调用。 
  88.          * 
  89.          * 如果你的申请不应该被安装 
  90.          *  
  91.          * 在设备没有自动对焦,您必须声明,您的应用程序 
  92.          *  
  93.          * 使用自动对焦 
  94.          * 
  95.          * <a href="{@docRoot} 
  96.          * guide/topics/manifest/uses-feature-element.html 
  97.          * "><uses-feature></a> 
  98.          * 
  99.          * manifest element. 
  100.          *  
  101.          * 如果当前闪光模式不 
  102.          * {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, 
  103.          *  
  104.          * 在自动对焦时,根据驾驶和相机的硬件 
  105.          *  
  106.          * 自动曝光锁定 
  107.          * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} 
  108.          *  
  109.          * 不要在自动对焦和之后的变化。但自动对焦程序可能会停止 
  110.          *  
  111.          * 自动曝光和自动白平衡在聚焦过程中瞬时。 
  112.          *  
  113.          * 停止预览 {@link #stopPreview()} 
  114.          *  
  115.          * 或触发仍然 
  116.          *  
  117.          * 图像捕捉 
  118.          * {@link #takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback)} 
  119.          * , 
  120.          *  
  121.          * 不会改变 
  122.          *  
  123.          * 焦点位置。应用程序必须调用cancelautofocus重置 
  124.          * 
  125.          * 
  126.          * 如果对焦成功,可以考虑使用 * {@link android.media.MediaActionSound} 正确播放自动对焦 
  127.          *  
  128.          * 成功的声音给用户。 
  129.          * 
  130.          * 
  131.          ** @param CB回调运行 
  132.          *  
  133.          * @看 cancelautofocus() 
  134.          *  
  135.          * @看  
  136.          *    android.hardware.Camera.Parameters#setAutoExposureLock(boolean) 
  137.          *  
  138.          * @看 android.hardware.Camera.Parameters#setAutoWhiteBalanceLock( 
  139.          *    boolean) 
  140.          *  
  141.          *    看 android.media.MediaActionSound 
  142.          */  
  143.         camera.autoFocus(autoFocusCallback);  
  144.     }  
  145. }  

   下面   下

   AutoFocusCallback 相机监听接口,对焦完成发送Handler消息是通知

  1. /** 
  2.  *  
  3.  * 回调接口用来通知自动对焦完成 
  4.  *  
  5.  * 不支持自动对焦的设备 将返回 boolean 类型的值假 
  6.  *  
  7.  * 回调到这个接口。如果您的应用程序需要自动对焦和 不应安装在设备没有自动对焦,您必须 声明你的应用程序使用 
  8.  * 
  9.  * Android 相机自动对焦源码请参考 <a href="{@docRoot} 
  10.  * guide/topics/manifest/uses-feature-element.html"><uses-feature></a> 
  11.  * manifest element.</p> 
  12.  *  
  13.  * 看#自动对焦 AutoFocusCallback 
  14.  *  
  15.  * 不建议使用新{@link android.hardware.camera2} API的新硬件应用。 
  16.  * 
  17.  */  
  18. final class AutoFocusCallback implements Camera.AutoFocusCallback {  
  19.   
  20.     private static final String TAG = AutoFocusCallback.class.getSimpleName();  
  21.   
  22.     // 自动对焦区间MS  
  23.     private static final long AUTOFOCUS_INTERVAL_MS = 1500L;  
  24.   
  25.     private Handler autoFocusHandler;  
  26.     private int autoFocusMessage;  
  27.   
  28.     void setHandler(Handler autoFocusHandler, int autoFocusMessage) {  
  29.         this.autoFocusHandler = autoFocusHandler;  
  30.         this.autoFocusMessage = autoFocusMessage;  
  31.     }  
  32.   
  33.     /** 
  34.      * 当你把摄像机自动对焦完成时。如果相机 
  35.      *  
  36.      * 如果相机不支持自动对焦和自动对焦,将会调用  onautofocus 将值立即回传  
  37.      *  
  38.      * <code>成功< /code>设置为<code>真< /code> 否则为假 
  39.      * 。 
  40.      * 
  41.      * 自动对焦程序不会自动曝光和自动白色 平衡完成后。 
  42.      * 
  43.      * @param成功真正的病灶是否成功,如果不假 
  44.      *  
  45.      * @param相机相机服务对象 
  46.      *  
  47.      * @看到Android的硬件。相机参数# setautoexposurelock(布尔)。 
  48.      *  
  49.      * @看到Android的硬件。相机参数# setautowhitebalancelock(布尔)。 
  50.      */  
  51.     @Override  
  52.     public void onAutoFocus(boolean success, Camera camera) {  
  53.         if (autoFocusHandler != null) {  
  54.               
  55.               
  56.             Message message = autoFocusHandler.obtainMessage(autoFocusMessage,  
  57.                     success);  
  58.             autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);  
  59.             autoFocusHandler = null;  
  60.         } else {  
  61.             Log.d(TAG, "自动对焦回调,但没有处理程序");  
  62.         }  
  63.     }  
  64.   
  65. }  


  PreviewCallback类监听接口,onPreviewFrame()函数:预览帧显示,拿到相机捕捉的画面和返回的byte[]字节数据。

  1. /** 
  2.  * 用于提供预览帧的副本的回调接口 
  3.  *  
  4.  * 他们被显示。 
  5.  * 
  6.  *  
  7.  * “看# PreviewCallback(Camera.PreviewCallback) 
  8.  *  
  9.  * “看# OneShotPreviewCallback(Camera.PreviewCallback) 
  10.  *  
  11.  * “看# PreviewCallbackWithBuffer(Camera.PreviewCallback) 
  12.  *  
  13.  * “看# startPreview() 
  14.  *  
  15.  * 
  16.  *  
  17.  * @deprecated 我们建议使用新的 {@link android.hardware.camera2} 新应用程序接口。 
  18.  * 
  19.  */  
  20. final class PreviewCallback implements Camera.PreviewCallback {  
  21.   
  22.     private static final String TAG = PreviewCallback.class.getSimpleName();  
  23.   
  24.     private final CameraConfigurationManager configManager;  
  25.   
  26.     // 使用一次预览回调  
  27.     private final boolean useOneShotPreviewCallback;  
  28.     private Handler previewHandler;  
  29.     private int previewMessage;  
  30.   
  31.     PreviewCallback(CameraConfigurationManager configManager,  
  32.             boolean useOneShotPreviewCallback) {  
  33.         this.configManager = configManager;  
  34.         this.useOneShotPreviewCallback = useOneShotPreviewCallback;  
  35.     }  
  36.   
  37.     /** 
  38.      *  
  39.      * @param previewHandler 
  40.      *            预览处理程序 
  41.      * @param previewMessage 
  42.      *            预览信息 
  43.      */  
  44.     void setHandler(Handler previewHandler, int previewMessage) {  
  45.         this.previewHandler = previewHandler;  
  46.         this.previewMessage = previewMessage;  
  47.     }  
  48.   
  49.     /** 
  50.      * 称为预览帧显示。调用这个回调在事件线程 {@link #open(int)}被称为。 
  51.      *  
  52.      * 如果使用 {@link android.graphics.ImageFormat#YV12} 
  53.      *  
  54.      * 格式的图形,参见方程 {@link Camera.Parameters#setPreviewFormat} 
  55.      *  
  56.      * 在预览回拨中的像素数据的安排 
  57.      *  
  58.      * 缓冲区 
  59.      * 
  60.      * @param 数据定义的格式的预览帧的内容 
  61.      *            通过 {@link android.graphics.ImageFormat},可以查询 具有 
  62.      *            {@link android.hardware.Camera.Parameters#getPreviewFormat()}. 
  63.      *  
  64.      *            如果 
  65.      *            {@link android.hardware.Camera.Parameters#setPreviewFormat(int)} 
  66.      *            永远不会被调用,默认的是 YCbCr_420_SP (NV21) .format 
  67.      * @param camera 
  68.      *            相机服务对象。 
  69.      */  
  70.     public void onPreviewFrame(byte[] data, Camera camera) {  
  71.           
  72.         // 获取相机分辨率  
  73.         Point cameraResolution = configManager.getCameraResolution();  
  74.           
  75.           
  76.         if (!useOneShotPreviewCallback) {  
  77.             camera.setPreviewCallback(null);  
  78.         }  
  79.           
  80.           
  81.         if (previewHandler != null) {  
  82.             Message message = previewHandler.obtainMessage(previewMessage,  
  83.                     cameraResolution.x, cameraResolution.y, data);  
  84.             message.sendToTarget();  
  85.             previewHandler = null;  
  86.         } else {  
  87.             Log.d(TAG, "预览回调,但没有处理程序");  
  88.             Log.d(TAG, "Got preview callback, but no handler for it");  
  89.         }  
  90.     }  
  91.   
  92. }  

  接下来可以通过相机拿到屏幕相关参数,来处理捕捉到的数据,getFramingRect()通过计算屏幕分辨率啦计算坐标位置,

getFramingRectInPreview()函数还是在计算坐标,buildLuminanceSource()函数非常重要,功能就是拿到YUV预览框宽高。在指定的Rect坐标内进行剪裁,拿到预览字符串判断剪裁大小,最后生成 PlanarYUVLuminanceSource
类对象,这个类会将结果生成 Bitmap,该函数在 DecodeHandler类中调用,用于计算要扫描成功后要捕捉的图片。

  1. /** 
  2.  * 计算框架矩形的界面应显示用户的位置 条码。这个目标有助于调整以及迫使用户持有该设备 足够远,以确保图像将集中。 
  3.  * 
  4.  * @return “返回”矩形在窗口坐标中绘制。 
  5.  */  
  6. public Rect getFramingRect() {  
  7.       
  8.      //获取屏幕分辨率  
  9.     Point screenResolution = configManager.getScreenResolution();  
  10.       
  11.     //Rect framingRect 直接适用于矩形四整数坐标。矩形  
  12.     if (framingRect == null) {  
  13.         if (camera == null) {  
  14.             return null;  
  15.         }  
  16.         int width = screenResolution.x * 3 / 4;  
  17.           
  18.         //当宽度最小框宽度  
  19.         if (width < MIN_FRAME_WIDTH) {  
  20.             width = MIN_FRAME_WIDTH;  
  21.         } else if (width > MAX_FRAME_WIDTH) {  
  22.             width = MAX_FRAME_WIDTH;  
  23.         }  
  24.           
  25.         int height = screenResolution.y * 3 / 4;  
  26.         //当高度小于最小高度  
  27.         if (height < MIN_FRAME_HEIGHT) {  
  28.             height = MIN_FRAME_HEIGHT;  
  29.         } else if (height > MAX_FRAME_HEIGHT) {  
  30.             height = MAX_FRAME_HEIGHT;  
  31.         }  
  32.           
  33.         int leftOffset = (screenResolution.x - width) / 2;  
  34.         int topOffset = (screenResolution.y - height) / 2;  
  35.           
  36.         //重构一个坐标  
  37.         framingRect = new Rect(leftOffset, topOffset, leftOffset + width,  
  38.                 topOffset + height);  
  39.         Log.d(TAG, "Calculated framing rect: " + framingRect);  
  40.     }  
  41.     return framingRect;  
  42. }  

  1. /** 
  2.      * 像 {@link #getFramingRect} 但坐标从预览 
  3.      *  
  4.      * 帧,而不是用户界面/屏幕。 
  5.      */  
  6.     public Rect getFramingRectInPreview() {  
  7.         if (framingRectInPreview == null) {  
  8.             Rect rect = new Rect(getFramingRect());  
  9.               
  10.             // 获取相机分辨率  
  11.             Point cameraResolution = configManager.getCameraResolution();  
  12.               
  13.             // 获取屏幕分辨率  
  14.             Point screenResolution = configManager.getScreenResolution();  
  15.           
  16.             rect.left = rect.left * cameraResolution.y / screenResolution.x;  
  17.             rect.right = rect.right * cameraResolution.y / screenResolution.x;  
  18.             rect.top = rect.top * cameraResolution.x / screenResolution.y;  
  19.             rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;  
  20.             framingRectInPreview = rect;  
  21.         }  
  22.         return framingRectInPreview;  
  23.     }  

  1. /** 
  2.      * 一个建立在适当的luminancesource对象工厂方法 
  3.      *  
  4.      * 预览缓冲区的格式,被描述为camera.parameters。 
  5.      * 
  6.      * @param data 
  7.      *            数据预览框。 
  8.      * @param width 
  9.      *            宽度图像的宽度。 
  10.      * @param height 
  11.      *            高度图像的高度。 
  12.      * @返回 planaryuvluminancesource 实例。 
  13.      */  
  14.     public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data,  
  15.             int width, int height) {  
  16.         Rect rect = getFramingRectInPreview();  
  17.         int previewFormat = configManager.getPreviewFormat();  
  18.         String previewFormatString = configManager.getPreviewFormatString();  
  19.         switch (previewFormat) {  
  20.   
  21.         /** 
  22.          * 这是标准的安卓格式,所有设备都需要支持。 
  23.          *  
  24.          * 在理论上,这是我们唯一关心的。 
  25.          */  
  26.         case PixelFormat.YCbCr_420_SP:  
  27.   
  28.             /** 
  29.              * 这种格式从未在野外见过,但兼容 
  30.              *  
  31.              * 我们只关心 
  32.              *  
  33.              * 关于“关于”的,所以允许它。 
  34.              */  
  35.         case PixelFormat.YCbCr_422_SP:  
  36.             return new PlanarYUVLuminanceSource(data, width, height, rect.left,  
  37.                     rect.top, rect.width(), rect.height());  
  38.   
  39.         default:  
  40.   
  41.             /** 
  42.              * 三星的时刻不正确地使用这个变量,而不是 
  43.              *  
  44.              * “文本”版本。 
  45.              *  
  46.              * 幸运的是,它也有所有的数据前,所以我们可以阅读 
  47.              *  
  48.              * 它 
  49.              */  
  50.             if ("yuv420p".equals(previewFormatString)) {  
  51.                 return new PlanarYUVLuminanceSource(data, width, height,  
  52.                         rect.left, rect.top, rect.width(), rect.height());  
  53.             }  
  54.         }  
  55.         throw new IllegalArgumentException("Unsupported picture format: "  
  56.                 + previewFormat + '/' + previewFormatString);  
  57.     }  

  (4)讲到这里可能还是没有明白到底是如何解码的,接下进入解码DecodeHandler类,该类主要是提供了捕捉,二维码截图后的矩形图像,生成Bitmap图像。首先来看下构造函数,在哪里构造的,可能你并未发现,我告诉你在解码线程DecodeThread类 run()方法体中构造的,那在哪里调用的呢,就要看getHandler()函数在哪里有调用,看下图会发现我们有三处用到它,接下来细看每个位置。


  1. DecodeHandler(MipcaActivityCapture activity,  
  2.             Hashtable<DecodeHintType, Object> hints) {  
  3.         multiFormatReader = new MultiFormatReader();  
  4.         multiFormatReader.setHints(hints);  
  5.         this.activity = activity;  
  6.     }  

  1. @Override  
  2. public void run() {  
  3.     /** 
  4.      * 初始化当前线程作为一个活套。 
  5.      *  
  6.      * 这给了你一个机会来创建处理程序,然后引用 
  7.      *  
  8.      * 这活套,然后再开始循环。一定要打电话 
  9.      *  
  10.      * {@link #loop()} 调用此方法后,通过调用 {@link #quit()}. 
  11.      *  
  12.      * 如果当前计数等于零,则没有发生任何事情。 
  13.      */  
  14.     Looper.prepare();  
  15.   
  16.     handler = new DecodeHandler(activity, hints);  
  17.   
  18.     /** 
  19.      * decrements伯爵的闩锁释放所有等待的线程,如果在达到零计数。 
  20.      *  
  21.      * 如果当前计数大于零则递减。 
  22.      *  
  23.      * 如果新计数为零,则所有等待线程被重新启用 
  24.      *  
  25.      * 线程调度的目的。 
  26.      * 
  27.      * 如果当前计数等于零,则没有发生任何事情。 
  28.      */  
  29.     handlerInitLatch.countDown();  
  30.   
  31.     /** 
  32.      * 调用此方法后,通过调用 
  33.      *  
  34.      * {@link #quit()} 结束循环。 
  35.      */  
  36.     Looper.loop();  
  37. }  


  1. Handler getHandler() {  
  2.         try {  
  3.   
  4.             /** 
  5.              * 导致当前线程等待锁存已计数的零,除非该线程是 {@linkplain Thread#interrupt interrupted}. 
  6.              *  
  7.              *  
  8.              *  
  9.              * 如果当前计数为零,则此方法立即返回。 如果当前计数大于零,则电流 
  10.              *  
  11.              * 线程成为禁用线程调度的目的和谎言休眠,直到有两点发生: 
  12.              *  
  13.              * 计数达到零由于调用的 {@link #countDown} 法;或 其他线程 { @linkplain 
  14.              * Thread#interrupt interrupts} 
  15.              * 当前线程。 
  16.              */  
  17.             handlerInitLatch.await();  
  18.         } catch (InterruptedException ie) {  
  19.             // continue?  
  20.         }  
  21.         return handler;  
  22.     }  

   (4.1)我们来看第一处调用 handleMessage(Message message),代码块,这里调用了     CameraManager.get().requestPreviewFrame()类函数,接下来进入这个函数,看到这个代码块是不是很惊讶发现这是之前看到的模块,现在知道这个Handler是被谁调用的,被谁发消息的了吧,被PreviewCallback类监听接口发出的消息。

  1. case R.id.decode_failed:  
  2.             /** 
  3.              * 我们尽可能快地解码,所以当一个解码失败,开始另一个。 
  4.              */  
  5.             state = State.PREVIEW;  
  6.             CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),  
  7.                     R.id.decode);  
  8.             break;  

  1. /** 
  2.      * 一个单独的预览框将返回给处理程序提供的处理程序。数据将作为字节到达 
  3.      * 在message.obj场,宽度和高度编码为message.arg1和message.arg2, 分别。 
  4.      * 
  5.      * @param handler 
  6.      *            发送消息的处理程序 
  7.      *  
  8.      * @param message 
  9.      *            要发送的消息的字段。 
  10.      *  
  11.      */  
  12.     public void requestPreviewFrame(Handler handler, int message) {  
  13.         if (camera != null && previewing) {  
  14.             previewCallback.setHandler(handler, message);  
  15.             if (useOneShotPreviewCallback) {  
  16.   
  17.                 /** 
  18.                  * 安装在下一个预览帧中调用的回调函数 
  19.                  *  
  20.                  * 除了在屏幕上显示。一次调用之后 
  21.                  *  
  22.                  * 回调被清除。这种方法可以称为任何时间,甚至当 
  23.                  *  
  24.                  * 预览是活的。其他预览回调重写 
  25.                  *  
  26.                  * 如果你使用的是预览数据来创建视频或静止图像, 
  27.                  *  
  28.                  * 强烈考虑使用 {@link android.media.MediaActionSound} 
  29.                  * 
  30.                  * 正确地显示图像捕捉或记录开始/停止给用户。 
  31.                  */  
  32.                 camera.setOneShotPreviewCallback(previewCallback);  
  33.             } else {  
  34.   
  35.                 /** 
  36.                  * 安装一个回调以供每个预览帧调用 
  37.                  *  
  38.                  * 在屏幕上显示。这个回调将被反复调用 
  39.                  *  
  40.                  * 只要预览是活动的。这种方法可以随时调用, 
  41.                  *  
  42.                  * 即使预览是活的。其他预览回调 
  43.                  *  
  44.                  * 重写。 
  45.                  * 
  46.                  * 如果你使用的是预览数据来创建视频或静止图像, 
  47.                  *  
  48.                  * 强烈考虑使用 {@link android.media.MediaActionSound} 
  49.                  * 
  50.                  * 正确地显示图像捕捉或记录开始/停止给用户 
  51.                  * 
  52.                  ** @param 可接收每个预览帧的副本的回调对象 
  53.                  *            , 
  54.                  *  
  55.                  * @see看 android.media.MediaActionSound 
  56.                  * 
  57.                  */  
  58.                 camera.setPreviewCallback(previewCallback);  
  59.             }  
  60.         }  
  61.     }  

   (4.2)我们来看第二处调用,quitSynchronously()这个函数也不陌生,这是退出时调用的

  1. /** 
  2.      * 退出同步 
  3.      */  
  4.     public void quitSynchronously() {  
  5.         state = State.DONE;  
  6.   
  7.         // 告诉相机停止绘制预览帧。  
  8.         CameraManager.get().stopPreview();  
  9.   
  10.         Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);  
  11.   
  12.         /** 
  13.          * 
  14.          * 将此消息发送到指定的处理程序 {@link #getTarget}. 如果该字段未被设置,则会引发一个空指针异常。 
  15.          */  
  16.         quit.sendToTarget();  
  17.         try {  
  18.   
  19.             /** 
  20.              * 阻止当前线程 <code>Thread.currentThread()</code>) 接收器完成它的执行和死亡。 
  21.              *  
  22.              * @throws InterruptedException 
  23.              *  如果当前线程被中断。 当前线程的中断状态将在异常之前被清除 
  24.              *  
  25.              * @see Object#notifyAll 
  26.              * @see java.lang.ThreadDeath 
  27.              */  
  28.             decodeThread.join();  
  29.         } catch (InterruptedException e) {  
  30.             // continue  
  31.         }  
  32.   
  33.         // 绝对肯定我们不会发送任何排队的消息  
  34.         removeMessages(R.id.decode_succeeded);  
  35.         removeMessages(R.id.decode_failed);  
  36.     }  

   (4.3)我们来看第三处调用,restartPreviewAndDecode()重新启动预览和解码函数,其实执行的代码还是,(4.1)中讲到的 PreviewCallback监听接口,发送的Handler消息。

  1. /** 
  2.      * 重新启动预览和解码 
  3.      */  
  4.     private void restartPreviewAndDecode() {  
  5.         if (state == State.SUCCESS) {  
  6.             state = State.PREVIEW;  
  7.             CameraManager.get().requestPreviewFrame(decodeThread.getHandler(),  
  8.                     R.id.decode);  
  9.             CameraManager.get().requestAutoFocus(this, R.id.auto_focus);  
  10.             activity.drawViewfinder();  
  11.         }  
  12.     }  


   (5)看完了相机解码流程,接下来看解码格式管理类 DecodeFormatManager类,该类封装了常用的一些条形码,二维码,商品码等一些格式结合。该类的其他几个函数并未使用,这里不做讲解。


  1. // Pattern.compile(",") 返回一个编译的形式给定正则表达式}  
  2. private static final Pattern COMMA_PATTERN = Pattern.compile(",");  
  3.   
  4. // 产品格式  
  5. static final Vector<BarcodeFormat> PRODUCT_FORMATS;  
  6.   
  7. // 一维码  
  8. static final Vector<BarcodeFormat> ONE_D_FORMATS;  
  9.   
  10. // QR码格式  
  11. static final Vector<BarcodeFormat> QR_CODE_FORMATS;  
  12.   
  13. // 数据矩阵格式  
  14. static final Vector<BarcodeFormat> DATA_MATRIX_FORMATS;  
  15.   
  16. static {  
  17.     PRODUCT_FORMATS = new Vector<BarcodeFormat>(5);  
  18.     PRODUCT_FORMATS.add(BarcodeFormat.UPC_A); // UPC标准码(通用商品)  
  19.     PRODUCT_FORMATS.add(BarcodeFormat.UPC_E); // UPC缩短码(商品短码)  
  20.     PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);  
  21.     PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);  
  22.     PRODUCT_FORMATS.add(BarcodeFormat.RSS14);  
  23.   
  24.     ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 4);  
  25.     ONE_D_FORMATS.addAll(PRODUCT_FORMATS); // 此处将PRODUCT_FORMATS中添加的码加入  
  26.     ONE_D_FORMATS.add(BarcodeFormat.CODE_39);  
  27.     ONE_D_FORMATS.add(BarcodeFormat.CODE_93);  
  28.     ONE_D_FORMATS.add(BarcodeFormat.CODE_128);  
  29.     ONE_D_FORMATS.add(BarcodeFormat.ITF);  
  30.   
  31.     QR_CODE_FORMATS = new Vector<BarcodeFormat>(1);// QR_CODE即二维码  
  32.     QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);  
  33.   
  34.     DATA_MATRIX_FORMATS = new Vector<BarcodeFormat>(1); // 也属于一种二维码  
  35.     DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);  
  36. }  

   (6)最后讲下相机如何配置 CameraConfigurationManager 相机配置管理器,改类中用到相机服务类 Camera.Parameters,这个类提供了相机设置,变焦等相关功能,注意这里用到很多parameters.get()函数,因为Parameters类中封装了一个字典,用于配置相机相关数据。改类提供了诸多函数和算法,但是最主要的功能是对外提供了相机分辨率 getCameraResolution()函数,屏幕分辨率getScreenResolution() 函数,预览格式getPreviewFormat()函数,获取预览格式字符串 getPreviewFormatString()函数等。其余函数是相关算法可以看代码理解

  1. /** 
  2.  *  
  3.  * 相机配置管理器 
  4.  *  
  5.  * @author ZQY 
  6.  * 
  7.  */  
  8. /** 
  9.  * @author Administrator 
  10.  * 
  11.  */  
  12. final class CameraConfigurationManager {  
  13.   
  14.     private static final String TAG = CameraConfigurationManager.class  
  15.             .getSimpleName();  
  16.   
  17.     // 所需的变焦  
  18.     private static final int TEN_DESIRED_ZOOM = 27;  
  19.   
  20.     // 所需的锐度  
  21.     private static final int DESIRED_SHARPNESS = 30;  
  22.   
  23.     /** 
  24.      * 返回一个编译的形式给定正则表达式 
  25.      * 
  26.      * @throws PatternSyntaxException 
  27.      *             如果正则表达式语法不正确,将会引发 PatternSyntaxException 异常。 
  28.      * 
  29.      *             compile(String regularExpression, int flags) 此方法被重载 
  30.      *             (regularExpression)正则表达式 可参考一下常量值设置 flags // *@see CANON_EQ 
  31.      *             // * @see CASE_INSENSITIVE // * @see COMMENTS // * @see 
  32.      *             #DOTALL // * @see #LITERAL // * @see #MULTILINE // * @see 
  33.      *             #UNICODE_CASE // * @see #UNIX_LINES 
  34.      */  
  35.     private static final Pattern COMMA_PATTERN = Pattern.compile(",");  
  36.   
  37.     private final Context context;  
  38.   
  39.     // 屏幕分辨率  
  40.     private Point screenResolution;  
  41.   
  42.     // 相机的分辨率  
  43.     private Point cameraResolution;  
  44.   
  45.     // 预览格式  
  46.     private int previewFormat;  
  47.   
  48.     // 预览格式字符串  
  49.     private String previewFormatString;  
  50.   
  51.     CameraConfigurationManager(Context context) {  
  52.         this.context = context;  
  53.     }  
  54.   
  55.     /** 
  56.      * 读,一时间,从应用程序所需要的相机的值。 
  57.      */  
  58.     void initFromCameraParameters(Camera camera) {  
  59.   
  60.         /** 
  61.          * 返回此相机服务的当前设置。 如果对返回的参数进行修改,则必须通过 to 
  62.          * {@link #setParameters(Camera.Parameters)} 设置 
  63.          *  
  64.          * see #setParameters(Camera.Parameters) 调用此方法设置 
  65.          */  
  66.         Camera.Parameters parameters = camera.getParameters();  
  67.   
  68.         /** 
  69.          * 返回预览帧的图像格式 
  70.          *  
  71.          * {@链接previewcallback }。 
  72.          *  
  73.          * @return 返回预览格式。 看android.graphics.imageformat 看# setpreviewformat 
  74.          */  
  75.         previewFormat = parameters.getPreviewFormat();  
  76.   
  77.         /** 
  78.          * 返回字符串参数的值。 
  79.          *  
  80.          * @param参数名的关键的关键 返回参数的字符串值 
  81.          */  
  82.         previewFormatString = parameters.get("preview-format");  
  83.   
  84.         // “默认预览格式:”+“预览格式”+“/”预览格式字符串 b  
  85.         Log.d(TAG, "Default preview format: " + previewFormat + '/'  
  86.                 + previewFormatString);  
  87.   
  88.         // 获取 应用程序的界面和窗口管理器对话。  
  89.         WindowManager manager = (WindowManager) context  
  90.                 .getSystemService(Context.WINDOW_SERVICE);  
  91.   
  92.         /** 
  93.          * Display 类解释 
  94.          *  
  95.          * 提供逻辑显示的大小和密度的信息。 
  96.          *  
  97.          * 显示区域以不同的方式描述。 
  98.          *  
  99.          * 用显示区域指定可能包含的显示的部分 
  100.          *  
  101.          * 一个应用程序窗口,不包括系统装饰。应用显示区域可以 
  102.          *  
  103.          * 小于真实的显示区域由于系统中减去所需空间 
  104.          *  
  105.          * 为装饰元素,如状态栏。使用下面的方法来查询 
  106.          *  
  107.          * *应用展示区 {@link #getSize}, {@link #getRectSize} and {@link #getMetrics} 
  108.          *  
  109.          * 真正显示区域指定包含内容的显示部分 
  110.          *  
  111.          * 包括系统装饰。即使如此,真正的显示区域可能比 
  112.          *  
  113.          * 物理尺寸的显示如果窗口管理器是模拟一个较小的显示 
  114.          *  
  115.          * 使用 (adb shell am display-size) 
  116.          *  
  117.          * 使用下面的方法来查询 
  118.          *  
  119.          * 真正的展示区: {@link #getRealSize}, {@link #getRealMetrics}. 
  120.          *  
  121.          * 逻辑显示并不一定代表一个特定的物理显示设备 
  122.          *  
  123.          * 如内置屏幕或外部显示器。逻辑的内容 
  124.          *  
  125.          * 显示可根据设备的一个或多个物理显示 
  126.          *  
  127.          * 这是当前连接的,以及是否已启用镜像。 
  128.          */  
  129.         Display display = manager.getDefaultDisplay();  
  130.   
  131.         // 屏幕分辨率  
  132.         screenResolution = new Point(display.getWidth(), display.getHeight());  
  133.   
  134.         // 打印屏幕分辨率值:screenResolution  
  135.         Log.d(TAG, "Screen resolution: " + screenResolution);  
  136.   
  137.         // 相机的分辨率  
  138.         cameraResolution = getCameraResolution(parameters, screenResolution);  
  139.   
  140.         // 相机分辨率:screenResolution  
  141.         Log.d(TAG, "Camera resolution: " + screenResolution);  
  142.     }  
  143.   
  144.     /** 
  145.      * 设置相机拍摄的图像,用于预览 
  146.      *  
  147.      * 解码。我们在这里检测到预览格式 
  148.      *  
  149.      * buildluminancesource()可以建立一个适当的luminancesource类。 
  150.      *  
  151.      * 在未来,我们可能想力yuv420sp因为它是最小的,和 
  152.      *  
  153.      * 在某些情况下,可以使用平面的条形码扫描而无需复制。 
  154.      */  
  155.     void setDesiredCameraParameters(Camera camera) {  
  156.   
  157.         /** 
  158.          * 返回此相机服务的当前设置。 
  159.          *  
  160.          * 如果对返回的参数进行修改,则必须通过 
  161.          *  
  162.          * {@link #setParameters(Camera.Parameters)} 设置生效 
  163.          *  
  164.          * 看 see #setParameters(Camera.Parameters) 的参数是 Camera.Parameters 
  165.          */  
  166.         Camera.Parameters parameters = camera.getParameters();  
  167.   
  168.         // 设置预览大小 cameraResolution  
  169.         Log.d(TAG, "Setting preview size: " + cameraResolution);  
  170.   
  171.         /** 
  172.          * 设置预览图片的尺寸。如果预览已经 
  173.          *  
  174.          * 开始,应用程序应在更改前先停止预览 
  175.          *  
  176.          * 预览大小。 宽度和高度的双方都是以相机为基础的。那 
  177.          *  
  178.          * 是,预览大小是在它被显示旋转之前的大小 
  179.          *  
  180.          * 定位。所以应用程序需要考虑显示方向 
  181.          *  
  182.          * 设置预览大小。例如,假设相机支持 
  183.          *  
  184.          * 尺寸320x480 480x320和预览。应用程序需要一个3:2 
  185.          *  
  186.          * 预览比。如果显示方向设置为0或180,则预览 
  187.          *  
  188.          * 大小应设置为480x320。如果显示方向被设置为 
  189.          *  
  190.          * 90或270,预览大小应设置为320x480。显示 
  191.          *  
  192.          * 设置图片大小时也应考虑*方向 
  193.          *  
  194.          * 缩略图大小。 
  195.          *  
  196.          * * @param宽度的图片,像素 
  197.          *  
  198.          * @param高度像素的图片, 
  199.          *  
  200.          *                “看# setdisplayorientation(int) 
  201.          *  
  202.          *                “看# getcamerainfo(int,camerainfo) 
  203.          *  
  204.          *                “看# setpicturesize(int,int) 
  205.          *  
  206.          *                “看# setjpegthumbnailsize(int,int) 
  207.          */  
  208.         parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);  
  209.   
  210.         setFlash(parameters);  
  211.         setZoom(parameters);  
  212.   
  213.         // setSharpness(parameters);  
  214.         // modify here  
  215.   
  216.         // camera.setDisplayOrientation(90);  
  217.         // 兼容2.1  
  218.         setDisplayOrientation(camera, 90);  
  219.         camera.setParameters(parameters);  
  220.     }  
  221.   
  222.     /** 
  223.      * 获取相机分辨率 
  224.      *  
  225.      * @return 
  226.      */  
  227.     Point getCameraResolution() {  
  228.         return cameraResolution;  
  229.     }  
  230.   
  231.     /** 
  232.      *  
  233.      获取屏幕分辨率 
  234.      *  
  235.      * @return 
  236.      */  
  237.     Point getScreenResolution() {  
  238.         return screenResolution;  
  239.     }  
  240.   
  241.     /** 
  242.      * 获得预览格式 
  243.      *  
  244.      * @return 
  245.      */  
  246.     int getPreviewFormat() {  
  247.         return previewFormat;  
  248.     }  
  249.   
  250.     /** 
  251.      * 获取预览格式字符串 
  252.      *  
  253.      * @return 
  254.      */  
  255.     String getPreviewFormatString() {  
  256.         return previewFormatString;  
  257.     }  
  258.   
  259.     /** 
  260.      * 获取相机分辨率 
  261.      *  
  262.      * @param parameters 
  263.      *            相机服务设置 即相机参数设置类 
  264.      * @param screenResolution 
  265.      *            屏幕分辨率 
  266.      * @return 
  267.      */  
  268.     private static Point getCameraResolution(Camera.Parameters parameters,  
  269.             Point screenResolution) {  
  270.   
  271.         // 获取预览大小值  
  272.         String previewSizeValueString = parameters.get("preview-size-values");  
  273.   
  274.         // 如果值为空 重新获取  
  275.         if (previewSizeValueString == null) {  
  276.             previewSizeValueString = parameters.get("preview-size-value");  
  277.         }  
  278.   
  279.         // 相机的分辨率  
  280.         Point cameraResolution = null;  
  281.   
  282.         if (previewSizeValueString != null) {  
  283.   
  284.             // 打印 预览值参数  
  285.             Log.d(TAG, "preview-size-values parameter: "  
  286.                     + previewSizeValueString);  
  287.   
  288.             // 相机的分辨率      
  289.             cameraResolution = findBestPreviewSizeValue(previewSizeValueString,  
  290.                     screenResolution);  
  291.         }  
  292.   
  293.         if (cameraResolution == null) {  
  294.             /** 
  295.              * 确保相机分辨率为8,为屏幕可能不。 
  296.              */  
  297.             cameraResolution = new Point((screenResolution.x >> 3) << 3,  
  298.                     (screenResolution.y >> 3) << 3);  
  299.         }  
  300.   
  301.         return cameraResolution;  
  302.     }  
  303.   
  304.     /** 
  305.      * 找到最佳的预览大小值 
  306.      *  
  307.      * @param previewSizeValueString 
  308.      *            预览大小值字符串 
  309.      * @param screenResolution 
  310.      *            屏幕分辨率 
  311.      * @return 
  312.      */  
  313.     private static Point findBestPreviewSizeValue(  
  314.             CharSequence previewSizeValueString, Point screenResolution) {  
  315.   
  316.         int bestX = 0// 最好的X  
  317.         int bestY = 0// 最好的Y  
  318.         int diff = Integer.MAX_VALUE; // 最大值  
  319.   
  320.         // 已 (逗号,) 拆分预览大小值字符串  
  321.         for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {  
  322.   
  323.             previewSize = previewSize.trim();  
  324.   
  325.             /** 
  326.              * 返回给定代码点的第一个索引,或- 1。 
  327.              *  
  328.              * 搜索开始时并向移动 该字符串的结束。 
  329.              */  
  330.             int dimPosition = previewSize.indexOf('x');  
  331.   
  332.             if (dimPosition < 0) {  
  333.   
  334.                 // 如果值小于零 打印 坏的预览大小  
  335.                 Log.w(TAG, "Bad preview-size: " + previewSize);  
  336.                 continue;  
  337.             }  
  338.   
  339.             int newX;  
  340.             int newY;  
  341.             try {  
  342.                 // 拿到新的 X值 和 Y 值  
  343.                 newX = Integer.parseInt(previewSize.substring(0, dimPosition));  
  344.                 newY = Integer.parseInt(previewSize.substring(dimPosition + 1));  
  345.             } catch (NumberFormatException nfe) {  
  346.   
  347.                 // 如果异常 打印 坏的预览大小  
  348.                 Log.w(TAG, "Bad preview-size: " + previewSize);  
  349.                 continue;  
  350.             }  
  351.   
  352.             /** 
  353.              * Math.abs(int i) 返回参数的绝对值 
  354.              *  
  355.              * 如果参数是 {@code Integer.MIN_VALUE}, {@code Integer.MIN_VALUE} 返回 
  356.              */  
  357.             int newDiff = Math.abs(newX - screenResolution.x)  
  358.                     + Math.abs(newY - screenResolution.y);  
  359.   
  360.             if (newDiff == 0) {  
  361.                 bestX = newX;  
  362.                 bestY = newY;  
  363.                 break;  
  364.             } else if (newDiff < diff) {  
  365.                 bestX = newX;  
  366.                 bestY = newY;  
  367.                 diff = newDiff;  
  368.             }  
  369.   
  370.         }  
  371.   
  372.         /** 
  373.          * 如果 最好的X 最好的Y 都大于零 从新绘制 
  374.          */  
  375.         if (bestX > 0 && bestY > 0) {  
  376.             return new Point(bestX, bestY);  
  377.         }  
  378.         return null;  
  379.     }  
  380.   
  381.     /** 
  382.      * 找到最好的MOT缩放值 
  383.      *  
  384.      * @param stringValues 
  385.      *            字符串值 
  386.      * @param tenDesiredZoom 
  387.      *            所需的变焦 
  388.      * @return 
  389.      */  
  390.     private static int findBestMotZoomValue(CharSequence stringValues,  
  391.             int tenDesiredZoom) {  
  392.   
  393.         int tenBestValue = 0;  
  394.   
  395.         // 以 (逗号,) 拆分字符串值  
  396.         for (String stringValue : COMMA_PATTERN.split(stringValues)) {  
  397.   
  398.             stringValue = stringValue.trim();  
  399.   
  400.             double value;  
  401.             try {  
  402.   
  403.                 // 得到整数值  
  404.                 value = Double.parseDouble(stringValue);  
  405.   
  406.             } catch (NumberFormatException nfe) {  
  407.                 return tenDesiredZoom;  
  408.             }  
  409.   
  410.             // 计算 改值得 十倍值  
  411.             int tenValue = (int) (10.0 * value);  
  412.   
  413.             // 计算绝对值 得到最好的值  
  414.             if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom  
  415.                     - tenBestValue)) {  
  416.   
  417.                 tenBestValue = tenValue;  
  418.             }  
  419.         }  
  420.         return tenBestValue;  
  421.     }  
  422.   
  423.     /** 
  424.      * 设置闪光 
  425.      *  
  426.      * @param parameters 
  427.      *            相机配置参数设置类 
  428.      */  
  429.     private void setFlash(Camera.Parameters parameters) {  
  430.   
  431.         // FIXME:这是一个黑客把闪光灯关掉了三星Galaxy。  
  432.         // 这是一个黑客攻击,以解决不同的价值  
  433.         // 看一看  
  434.         // 限制看二检查蛋糕,每三星的建议  
  435.         // if (Build.MODEL.contains("Behold II") &&  
  436.         // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {  
  437.         if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3  
  438.             // =  
  439.             // Cupcake  
  440.             parameters.set("flash-value"1);  
  441.         } else {  
  442.             parameters.set("flash-value"2);  
  443.         }  
  444.   
  445.         /** 
  446.          * 这是标准的设置,把所有的设备应该遵守 
  447.          */  
  448.         parameters.set("flash-mode""off");  
  449.     }  
  450.   
  451.     /** 
  452.      * 设定缩放等级 
  453.      *  
  454.      * @param parameters 
  455.      *            相机配置参数设置类 
  456.      */  
  457.     private void setZoom(Camera.Parameters parameters) {  
  458.   
  459.         // 拿到 变焦支持 值  
  460.         String zoomSupportedString = parameters.get("zoom-supported");  
  461.   
  462.         // 判断 zoomSupportedString 值不为空 且 字符串不为 boolean 类型的值  
  463.         if (zoomSupportedString != null  
  464.                 && !Boolean.parseBoolean(zoomSupportedString)) {  
  465.             return;  
  466.         }  
  467.   
  468.         // 所需的变焦  
  469.         int tenDesiredZoom = TEN_DESIRED_ZOOM;  
  470.   
  471.         // 得到 最大变焦  
  472.         String maxZoomString = parameters.get("max-zoom");  
  473.   
  474.         if (maxZoomString != null) {  
  475.             try {  
  476.   
  477.                 // 得到最大变焦值 10 倍值  
  478.                 int tenMaxZoom = (int) (10.0 * Double  
  479.                         .parseDouble(maxZoomString));  
  480.   
  481.                 // 如果所需变焦值 大于 最大变焦值  
  482.                 if (tenDesiredZoom > tenMaxZoom) {  
  483.                     tenDesiredZoom = tenMaxZoom;  
  484.                 }  
  485.   
  486.             } catch (NumberFormatException nfe) {  
  487.   
  488.                 // 打印异常的变焦值  
  489.                 Log.w(TAG, "Bad max-zoom: " + maxZoomString);  
  490.             }  
  491.         }  
  492.   
  493.         // 图片缩放最大  
  494.         String takingPictureZoomMaxString = parameters  
  495.                 .get("taking-picture-zoom-max");  
  496.   
  497.         if (takingPictureZoomMaxString != null) {  
  498.   
  499.             try {  
  500.   
  501.                 // 最大缩放  
  502.                 int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);  
  503.   
  504.                 // 所需变焦 大于 图片最大缩放值  
  505.                 if (tenDesiredZoom > tenMaxZoom) {  
  506.                     tenDesiredZoom = tenMaxZoom;  
  507.                 }  
  508.   
  509.             } catch (NumberFormatException nfe) {  
  510.   
  511.                 // 异常的 图片缩放最大值  
  512.                 Log.w(TAG, "Bad taking-picture-zoom-max: "  
  513.                         + takingPictureZoomMaxString);  
  514.             }  
  515.         }  
  516.   
  517.         // MOT缩放值  
  518.         String motZoomValuesString = parameters.get("mot-zoom-values");  
  519.   
  520.         if (motZoomValuesString != null) {  
  521.   
  522.             tenDesiredZoom = findBestMotZoomValue(motZoomValuesString,  
  523.                     tenDesiredZoom);  
  524.         }  
  525.   
  526.         // mot 变焦步骤  
  527.         String motZoomStepString = parameters.get("mot-zoom-step");  
  528.   
  529.         if (motZoomStepString != null) {  
  530.             try {  
  531.   
  532.                 // MOT缩放值  
  533.                 double motZoomStep = Double.parseDouble(motZoomStepString  
  534.                         .trim());  
  535.   
  536.                 int tenZoomStep = (int) (10.0 * motZoomStep);  
  537.   
  538.                 if (tenZoomStep > 1) {  
  539.                     tenDesiredZoom -= tenDesiredZoom % tenZoomStep;  
  540.                 }  
  541.             } catch (NumberFormatException nfe) {  
  542.                 // continue  
  543.             }  
  544.         }  
  545.   
  546.         // 设置缩放。这有助于鼓励用户拉回来。  
  547.         // 一些设备,如有一个变焦参数  
  548.         if (maxZoomString != null || motZoomValuesString != null) {  
  549.             parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));  
  550.         }  
  551.   
  552.         // 大多数设备,像英雄,似乎暴露这个变焦参数。  
  553.         // 它的值“27”,似乎意味着2.7倍变焦  
  554.         if (takingPictureZoomMaxString != null) {  
  555.             parameters.set("taking-picture-zoom", tenDesiredZoom);  
  556.         }  
  557.     }  
  558.   
  559.     /** 
  560.      * 获得理想的清晰度 
  561.      *  
  562.      * @return 
  563.      */  
  564.     public static int getDesiredSharpness() {  
  565.         return DESIRED_SHARPNESS;  
  566.     }  
  567.   
  568.     /** 
  569.      *  
  570.      * 设置显示方向 
  571.      *  
  572.      * compatible 1.6 
  573.      *  
  574.      * @param camera 
  575.      * @param angle 
  576.      */  
  577.     protected void setDisplayOrientation(Camera camera, int angle) {  
  578.         Method downPolymorphic;  
  579.         try {  
  580.   
  581.             /** 
  582.              * 返回一个表示公共方法的 {@code Method}对象 
  583.              *  
  584.              * 指定的名称和参数类型。 
  585.              *  
  586.              * {@code (Class[]) null} 等于空数组 
  587.              *  
  588.              * 该方法首先搜索 C类的代码 {@code Class} 最后C的父类 
  589.              *  
  590.              * 为匹配名称的方法。 
  591.              * 
  592.              * 将调用不存在方法异常将会引发 @throws NoSuchMethodException 
  593.              *  
  594.              * 看 see #getDeclaredMethod(String, Class[]) 
  595.              */  
  596.             downPolymorphic = camera.getClass().getMethod(  
  597.                     "setDisplayOrientation"new Class[] { int.class });  
  598.   
  599.             if (downPolymorphic != null)  
  600.                 /** 
  601.                  * 返回动态调用此方法的结果。相当于 
  602.                  * {@code receiver.methodName(arg1, arg2, ... , argN)}. 
  603.                  *  
  604.                  * 如果该方法是静态的,则忽略接收器参数(可能是空的) 
  605.                  *  
  606.                  * 如果该方法没有任何参数,您可以通过 {@code (Object[]) null} 来代替 分配一个空数组。 
  607.                  *  
  608.                  * 如果你调用一个可变参数的方法,你需要传递一个{@code Object[]}做,不是虚拟机,和 
  609.                  *  
  610.                  * 反射机制不会为您做此。(它不能,因为它会 
  611.                  *  
  612.                  * 暧昧。) 反射方法调用遵循通常的方法查找方法。 
  613.                  * 
  614.                  * 如果在调用过程中抛出异常,则该异常被捕获和 
  615.                  *  
  616.                  * 包装在一个invocationtargetexception。然后抛出此异常。 
  617.                  *  
  618.                  * 
  619.                  *  
  620.                  * 如果调用完成的话,返回值本身是 
  621.                  *  
  622.                  * 返回。如果该方法被声明为返回原始类型,则 
  623.                  *  
  624.                  * 返回值被装箱。如果返回类型无效,返回空 
  625.                  */  
  626.                 downPolymorphic.invoke(camera, new Object[] { angle });  
  627.   
  628.         } catch (Exception e1) {  
  629.         }  
  630.     }  
  631.   
  632. }  

   (7)最后讲下相机中散光灯如何使用,FlashlightManager类控制散光灯,该类提供了多种反射技术,拿到封装的对象和方法,来实现硬件功能,请看相关注解

  1. /** 
  2.  *  
  3.  这个类是用来激活弱光的一些相机的手机(不是闪光灯) 
  4.  *  
  5.  * 为了照亮扫描的表面。没有官方的方法来做这件事, 
  6.  *  
  7.  * 但是,允许访问此功能的类仍然存在于某些设备上。 
  8.  *  
  9.  * 因此通过大量的思考。 
  10.  * 
  11.  * 看 <a href= 
  12.  * "http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/" 
  13.  * > http://almondmendoza.com/2009/01/05/changing-the-screen-brightness- 
  14.  * programatically/</a> and <a href= 
  15.  * "http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java" 
  16.  * > http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo 
  17.  * /DroidLED.java</a>. 
  18.  *  
  19.  * 感谢Ryan Alford指出该类的可用性。 
  20.  */  
  21. final class FlashlightManager {  
  22.   
  23.     private static final String TAG = FlashlightManager.class.getSimpleName();  
  24.   
  25.     // 硬件服务  
  26.     private static final Object iHardwareService;  
  27.   
  28.     // 设置闪光功能的方法  
  29.     private static final Method setFlashEnabledMethod;  
  30.   
  31.     static {  
  32.         iHardwareService = getHardwareService();  
  33.         setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);  
  34.         if (iHardwareService == null) {  
  35.             Log.v(TAG, "该设备支持一个手电筒的控制");  
  36.         } else {  
  37.             Log.v(TAG, "该设备不支持控制手电筒");  
  38.         }  
  39.     }  
  40.   
  41.     private FlashlightManager() {  
  42.     }  
  43.   
  44.     /** 
  45.      * 控制相机闪光灯开关 
  46.      */  
  47.     // FIXME  
  48.     static void enableFlashlight() {  
  49.         setFlashlight(false);  
  50.     }  
  51.   
  52.     /** 
  53.      * 
  54.      禁用闪光灯 
  55.      */  
  56.     static void disableFlashlight() {  
  57.         setFlashlight(false);  
  58.     }  
  59.   
  60.     private static Object getHardwareService() {  
  61.   
  62.         // 反向映射 得到指定 类  
  63.         Class<?> serviceManagerClass = maybeForName("android.os.ServiceManager");  
  64.         if (serviceManagerClass == null) {  
  65.             return null;  
  66.         }  
  67.   
  68.         Method getServiceMethod = maybeGetMethod(serviceManagerClass,  
  69.                 "getService", String.class);  
  70.         if (getServiceMethod == null) {  
  71.             return null;  
  72.         }  
  73.   
  74.         Object hardwareService = invoke(getServiceMethod, null"hardware");  
  75.         if (hardwareService == null) {  
  76.             return null;  
  77.         }  
  78.   
  79.         Class<?> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");  
  80.         if (iHardwareServiceStubClass == null) {  
  81.             return null;  
  82.         }  
  83.   
  84.         Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass,  
  85.                 "asInterface", IBinder.class);  
  86.         if (asInterfaceMethod == null) {  
  87.             return null;  
  88.         }  
  89.   
  90.         return invoke(asInterfaceMethod, null, hardwareService);  
  91.     }  
  92.   
  93.     private static Method getSetFlashEnabledMethod(Object iHardwareService) {  
  94.         if (iHardwareService == null) {  
  95.             return null;  
  96.         }  
  97.         Class<?> proxyClass = iHardwareService.getClass();  
  98.         return maybeGetMethod(proxyClass, "setFlashlightEnabled"boolean.class);  
  99.     }  
  100.   
  101.     private static Class<?> maybeForName(String name) {  
  102.         try {  
  103.   
  104.             /** 
  105.              * 返回一个代表类的{ @码类}对象 
  106.              *  
  107.              * 给定的名称。名称应该是非本原的名称 
  108.              *  
  109.              * 类,如在{ @链接类定义}中所描述的。 
  110.              *  
  111.              * 原始类型不能使用此方法来找到;使用{ @代码 
  112.              *  
  113.              * int.class }或{ @代码类型}而不是整数。 
  114.              *  
  115.              * 
  116.              *  
  117.              * 如果尚未加载该类,则加载和初始化 
  118.              *  
  119.              * 第一。这是通过调用类的类装入器来完成的 
  120.              *  
  121.              * 或其母类装入器中的一个。这可能是一个静态初始化运行 
  122.              *  
  123.              * 这一呼叫的结果。 
  124.              *  
  125.              * 
  126.              *  
  127.              * @抛出ClassNotFoundException 
  128.              *  
  129.              *                           如果无法找到所需的类。 
  130.              *  
  131.              * @抛出连接失败错误 
  132.              *  
  133.              *           如果在连接过程中出现错误 
  134.              *  
  135.              * @投exceptionininitializererror 
  136.              *  
  137.              *                               如果在静态初始化期间发生异常 
  138.              *  
  139.              *                               类。 
  140.              */  
  141.             return Class.forName(name);  
  142.         } catch (ClassNotFoundException cnfe) {  
  143.             // OK  
  144.             return null;  
  145.         } catch (RuntimeException re) {  
  146.             Log.w(TAG, "Unexpected error while finding class " + name, re);  
  147.             return null;  
  148.         }  
  149.     }  
  150.   
  151.     private static Method maybeGetMethod(Class<?> clazz, String name,  
  152.             Class<?>... argClasses) {  
  153.         try {  
  154.             return clazz.getMethod(name, argClasses);  
  155.         } catch (NoSuchMethodException nsme) {  
  156.             // OK  
  157.             return null;  
  158.         } catch (RuntimeException re) {  
  159.             Log.w(TAG, "Unexpected error while finding method " + name, re);  
  160.             return null;  
  161.         }  
  162.     }  
  163.   
  164.     private static Object invoke(Method method, Object instance, Object... args) {  
  165.         try {  
  166.   
  167.             /** 
  168.              * 
  169.              *  
  170.              * 返回动态调用此方法的结果。相当于 
  171.              *  
  172.              * { @代码语句(arg1,arg2接收器,…argn)}。 
  173.              *  
  174.              * 
  175.              *  
  176.              * 如果该方法是静态的,则忽略接收器参数(可能是空的)。 
  177.              *  
  178.              * 
  179.              *  
  180.              * 如果该方法没有任何参数,您可以通过{ @代码(对象[)]空}来代替 
  181.              *  
  182.              * 分配一个空数组。 
  183.              *  
  184.              * 
  185.              *  
  186.              * <BR> 
  187.              * 如果你调用一个可变参数的方法,你需要传递一个{ } [ ] @代码对象的 
  188.              *  
  189.              * 变参数:转换通常是在{ @代码javac }做,不是虚拟机,和 
  190.              *  
  191.              * 反射机制不会为您做此。(它不能,因为它会 
  192.              *  
  193.              * 暧昧。) 
  194.              *  
  195.              * 
  196.              *  
  197.              * *反射方法调用遵循通常的方法查找方法。 
  198.              *  
  199.              * 
  200.              *  
  201.              * 如果在调用过程中抛出异常,则该异常被捕获和 
  202.              *  
  203.              * 包装在一个invocationtargetexception。然后抛出此异常。 
  204.              *  
  205.              * 
  206.              *  
  207.              * 如果调用完成的话,返回值本身是 
  208.              *  
  209.              * 返回。如果该方法被声明为返回原始类型,则 
  210.              *  
  211.              * 返回值被装箱。如果返回类型无效,返回空。 
  212.              *  
  213.              * 
  214.              *  
  215.              * @param接收机 
  216.              *  
  217.              *           将调用该方法的对象(或静态方法为空) 
  218.              *  
  219.              * @param参数 
  220.              *  
  221.              *          参数的方法 
  222.              *  
  223.              *          返回结果 
  224.              *  
  225.              * 
  226.              *  
  227.              * @抛出NullPointerException异常 
  228.              *  
  229.              *                           如果{“代码”接收器=空}为非静态方法 
  230.              *  
  231.              * @抛出非法存取异常 
  232.              *  
  233.              *           如果这个方法不容易(参阅{@链接AccessibleObject }) 
  234.              *  
  235.              * @抛出时 
  236.              *  
  237.              *      如果参数的数目与参数的数目不匹配,该接收器 
  238.              *  
  239.              *      与声明类不相容,或争论不能拆箱 
  240.              *  
  241.              *      或转换为相应的参数类型的拉宽转换 
  242.              *  
  243.              * @投invocationtargetexception 
  244.              *  
  245.              *                             如果被调用的方法引发异常 
  246.              *  
  247.              * 
  248.              */  
  249.             return method.invoke(instance, args);  
  250.         } catch (IllegalAccessException e) {  
  251.             Log.w(TAG, "Unexpected error while invoking " + method, e);  
  252.             return null;  
  253.         } catch (InvocationTargetException e) {  
  254.             Log.w(TAG, "Unexpected error while invoking " + method,  
  255.                     e.getCause());  
  256.             return null;  
  257.         } catch (RuntimeException re) {  
  258.             Log.w(TAG, "Unexpected error while invoking " + method, re);  
  259.             return null;  
  260.         }  
  261.     }  
  262.   
  263.     private static void setFlashlight(boolean active) {  
  264.         if (iHardwareService != null) {  
  265.             invoke(setFlashEnabledMethod, iHardwareService, active);  
  266.         }  
  267.     }  
  268.   
  269. }  

   (8)最后你会想如何实现扫描效果那个识别二位码控件如何实现,请看 ViewfinderView类,该类继承了View类,提供了绘制扫描控件 onDraw(Canvas canvas)函数

  1. @Override  
  2. public void onDraw(Canvas canvas) {  
  3.     // 中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改  
  4.     Rect frame = CameraManager.get().getFramingRect();  
  5.     if (frame == null) {  
  6.         return;  
  7.     }  
  8.   
  9.     // 初始化中间线滑动的最上边和最下边  
  10.     if (!isFirst) {  
  11.         isFirst = true;  
  12.         slideTop = frame.top;  
  13.         slideBottom = frame.bottom;  
  14.     }  
  15.   
  16.     // 获取屏幕的宽和高  
  17.     int width = canvas.getWidth();  
  18.     int height = canvas.getHeight();  
  19.   
  20.     paint.setColor(resultBitmap != null ? resultColor : maskColor);  
  21.   
  22.     // 画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面  
  23.     // 扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边  
  24.     canvas.drawRect(00, width, frame.top, paint);  
  25.     canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);  
  26.     canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,  
  27.             paint);  
  28.     canvas.drawRect(0, frame.bottom + 1, width, height, paint);  
  29.   
  30.     if (resultBitmap != null) {  
  31.         // Draw the opaque result bitmap over the scanning rectangle  
  32.         paint.setAlpha(OPAQUE);  
  33.         canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);  
  34.     } else {  
  35.   
  36.         // 画扫描框边上的角,总共8个部分  
  37.         paint.setColor(Color.GREEN);  
  38.         canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,  
  39.                 frame.top + CORNER_WIDTH, paint);  
  40.         canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH,  
  41.                 frame.top + ScreenRate, paint);  
  42.         canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,  
  43.                 frame.top + CORNER_WIDTH, paint);  
  44.         canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right,  
  45.                 frame.top + ScreenRate, paint);  
  46.         canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left  
  47.                 + ScreenRate, frame.bottom, paint);  
  48.         canvas.drawRect(frame.left, frame.bottom - ScreenRate, frame.left  
  49.                 + CORNER_WIDTH, frame.bottom, paint);  
  50.         canvas.drawRect(frame.right - ScreenRate, frame.bottom  
  51.                 - CORNER_WIDTH, frame.right, frame.bottom, paint);  
  52.         canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom  
  53.                 - ScreenRate, frame.right, frame.bottom, paint);  
  54.   
  55.         // 绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE  
  56.         slideTop += SPEEN_DISTANCE;  
  57.         if (slideTop >= frame.bottom) {  
  58.             slideTop = frame.top;  
  59.         }  
  60.         canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop  
  61.                 - MIDDLE_LINE_WIDTH / 2, frame.right - MIDDLE_LINE_PADDING,  
  62.                 slideTop + MIDDLE_LINE_WIDTH / 2, paint);  
  63.   
  64.         // 画扫描框下面的字  
  65.         paint.setColor(Color.WHITE);  
  66.         paint.setTextSize(TEXT_SIZE * density);  
  67.         paint.setAlpha(0x40);  
  68.         paint.setTypeface(Typeface.create("System", Typeface.BOLD));  
  69.         canvas.drawText(  
  70.                 getResources().getString(R.string.scan_text),  
  71.                 frame.left,  
  72.                 (float) (frame.bottom + (float) TEXT_PADDING_TOP * density),  
  73.                 paint);  
  74.   
  75.         Collection<ResultPoint> currentPossible = possibleResultPoints;  
  76.         Collection<ResultPoint> currentLast = lastPossibleResultPoints;  
  77.         if (currentPossible.isEmpty()) {  
  78.             lastPossibleResultPoints = null;  
  79.         } else {  
  80.             possibleResultPoints = new HashSet<ResultPoint>(5);  
  81.             lastPossibleResultPoints = currentPossible;  
  82.             paint.setAlpha(OPAQUE);  
  83.             paint.setColor(resultPointColor);  
  84.             for (ResultPoint point : currentPossible) {  
  85.                 canvas.drawCircle(frame.left + point.getX(), frame.top  
  86.                         + point.getY(), 6.0f, paint);  
  87.             }  
  88.         }  
  89.         if (currentLast != null) {  
  90.             paint.setAlpha(OPAQUE / 2);  
  91.             paint.setColor(resultPointColor);  
  92.             for (ResultPoint point : currentLast) {  
  93.                 canvas.drawCircle(frame.left + point.getX(), frame.top  
  94.                         + point.getY(), 3.0f, paint);  
  95.             }  
  96.         }  
  97.   
  98.         // 只刷新扫描框的内容,其他地方不刷新  
  99.         postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,  
  100.                 frame.right, frame.bottom);  
  101.   
  102.     }  
  103. }  


   (9)其中还有几个相关辅助类没有贴上,ViewfinderResultPointCallback类,PlanarYUVLuminanceSource类,InactivityTimer类,FinishListener类,Intents类 这些可以详细看代码

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值