Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android 利用ZXING掃描二維碼代碼分析

android 利用ZXING掃描二維碼代碼分析

編輯:關於Android編程

之前給公司做了一個攝影相關的應用,現在要添加二維碼掃描的功能,網上找資料後,雖然已經成功集成到app裡面,但是總感覺心裡沒底兒。所以趁這段時間不是很忙,總結一下。   首先是啟動掃描的UI類:   1,Activity啟動,當然是onCreate方法   private CaptureActivityHandler handler; private ViewfinderView viewfinderView; private boolean hasSurface; private Vector<BarcodeFormat> decodeFormats; private String characterSet; private InactivityTimer inactivityTimer; private MediaPlayer mediaPlayer; private boolean playBeep; private static final float BEEP_VOLUME = 0.10f; private boolean vibrate;   /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_capture);     // ViewUtil.addTopView(getApplicationContext(), this,     // R.string.scan_card);     CameraManager.init(getApplication());     viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);       Button mButtonBack = (Button) findViewById(R.id.button_back);     mButtonBack.setOnClickListener(new OnClickListener() {         @Override         public void onClick(View v) {             Scaner.this.finish();         }     });     hasSurface = false;     inactivityTimer = new InactivityTimer(this); }   @Override protected void onResume() {     super.onResume();     SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);     SurfaceHolder surfaceHolder = surfaceView.getHolder();     if (hasSurface) {         initCamera(surfaceHolder);     } else {         surfaceHolder.addCallback(this);         surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);     }     decodeFormats = null;     characterSet = null;       playBeep = true;     AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);     if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {         playBeep = false;     }     initBeepSound();     vibrate = true;   }   @Override protected void onPause() {     super.onPause();     if (handler != null) {         handler.quitSynchronously();         handler = null;     }     CameraManager.get().closeDriver(); }   @Override protected void onDestroy() {     inactivityTimer.shutdown();     super.onDestroy(); }   /**  *  *  *  * @param @param result * @param @param barcode   * @author  Administrator * @return void  */ public void handleDecode(Result result, Bitmap barcode) {     inactivityTimer.onActivity();     playBeepSoundAndVibrate();     String resultString = result.getText();     if (resultString.equals("")) {         Toast.makeText(Scaner.this, "Scan failed!",3000).show();     } else {         //查詢keycode 本地數據庫 1,優先查詢本地庫,2,沒有本地庫,直接跳到知道鏈接         //分析出keyCode         Log.i("testMain","scan_result=====>"+resultString);           String keyCode="";         String[] split1;         if(resultString.lastIndexOf("?")<0){             Intent intent = new Intent(this, InnerBrowser.class);             Bundle bundle = new Bundle();             bundle.putString("result", resultString);             //bundle.putParcelable("bitmap", barcode);             intent.putExtras(bundle);             startActivity(intent);Scaner.this.finish();return;         }         String[] attr = resultString.substring(resultString.lastIndexOf("?")-1, resultString.length()).split("&");         for (String string : attr) {              split1 = string.split("=");             if(split1[0].equalsIgnoreCase("keycode")){                 //找到                 if(split1.length==2){                     keyCode=split1[1];                 }             }         }         Log.i("testMain","keyCode=====>"+keyCode);         if(!StringUtils.isBlank(keyCode)){             AttractionDAO dao=new AttractionDAO(Scaner.this);             Attraction a=dao.findAttrByKeyCode(keyCode);             Log.i("testMain","a=====>"+a);             if(a!=null){                 Intent it=new Intent();                 it.setClass(Scaner.this, UIAttractionDetail.class);                 it.putExtra("a", a);                 startActivity(it);             }else{                 Intent intent = new Intent(this, InnerBrowser.class);                 Bundle bundle = new Bundle();                 bundle.putString("result", resultString);                 //bundle.putParcelable("bitmap", barcode);                 intent.putExtras(bundle);                 startActivity(intent);                 //this.setResult(RESULT_OK, resultIntent);                 //使用內置浏覽器打開網站內容             }         }else{             Intent intent = new Intent(this, InnerBrowser.class);             Bundle bundle = new Bundle();             bundle.putString("result", resultString);             //bundle.putParcelable("bitmap", barcode);             intent.putExtras(bundle);             startActivity(intent);             //this.setResult(RESULT_OK, resultIntent);             //使用內置浏覽器打開網站內容         }     }     Scaner.this.finish(); }   private void initCamera(SurfaceHolder surfaceHolder) {     try {         CameraManager.get().openDriver(surfaceHolder);     } catch (IOException ioe) {         return;     } catch (RuntimeException e) {         return;     }     if (handler == null) {         handler = new CaptureActivityHandler(this, decodeFormats,                 characterSet);     } }   @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,         int height) {   }   @Override public void surfaceCreated(SurfaceHolder holder) {     if (!hasSurface) {         hasSurface = true;         initCamera(holder);     }   }   @Override public void surfaceDestroyed(SurfaceHolder holder) {     hasSurface = false;   }   public ViewfinderView getViewfinderView() {     return viewfinderView; }   public Handler getHandler() {     return handler; }   public void drawViewfinder() {     viewfinderView.drawViewfinder();   }   private void initBeepSound() {     if (playBeep && mediaPlayer == null) {         // The volume on STREAM_SYSTEM is not adjustable, and users found it         // too loud,         // so we now play on the music stream.         setVolumeControlStream(AudioManager.STREAM_MUSIC);         mediaPlayer = new MediaPlayer();         mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);         mediaPlayer.setOnCompletionListener(beepListener);           AssetFileDescriptor file = getResources().openRawResourceFd(                 R.raw.beep);         try {             mediaPlayer.setDataSource(file.getFileDescriptor(),                     file.getStartOffset(), file.getLength());             file.close();             mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);             mediaPlayer.prepare();         } catch (IOException e) {             mediaPlayer = null;         }     } }   private static final long VIBRATE_DURATION = 200L;   private void playBeepSoundAndVibrate() {     if (playBeep && mediaPlayer != null) {         mediaPlayer.start();     }     if (vibrate) {         Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);         vibrator.vibrate(VIBRATE_DURATION);     } }   /**  * When the beep has finished playing, rewind to queue up another one.  */ private final OnCompletionListener beepListener = new OnCompletionListener() {     public void onCompletion(MediaPlayer mediaPlayer) {         mediaPlayer.seekTo(0);     } }; 從上面代碼可以看出,做了三件事兒:加載布局文件;初始化了一個相機管理器;設置按鈕監聽,初始化了一個InactivityTimer實例;   然後,最重要的是他實現了一個CallBack函數:具體參見:    SurfaceHolder.Callback 譯文 此時,   1 surfaceCreated 這個方法會調用然後就初始化相機的一些參數:     前兩個我們好理解,第三個是干嘛的?   我們先看布局文件:   <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="fill_parent"         android:layout_height="fill_parent" >     <RelativeLayout         android:layout_width="fill_parent"         android:layout_height="fill_parent" >           <SurfaceView             android:id="@+id/preview_view"             android:layout_width="fill_parent"             android:layout_height="fill_parent"             android:layout_gravity="center" />           <com.euc.app.scan.view.ViewfinderView             android:id="@+id/viewfinder_view"             android:layout_width="wrap_content"             android:layout_height="wrap_content" />           <include             android:id="@+id/include1"             android:layout_width="fill_parent"             android:layout_height="wrap_content"             android:layout_alignParentTop="true"             layout="@layout/activity_title" />     </RelativeLayout>   </FrameLayout> 可以看到裡面有一個自定義的View及surfaceView,   對於我這樣的初學者來說,surfaceView 是什麼東西?   csdn上看到這個文章   Android中SurfaceView的使用詳解 雖然不是很明白,但是大致明白這是個什麼東西了。   了解了生命周期之後,我們來看他執行的方法:     private void initCamera(SurfaceHolder surfaceHolder) {         try {             CameraManager.get().openDriver(surfaceHolder);//配置攝像頭         } catch (IOException ioe) {             return;         } catch (RuntimeException e) {             return;         }         if (handler == null) {             handler = new CaptureActivityHandler(this, decodeFormats,                     characterSet);//初始化方法裡面開啟攝像頭預覽界面。         }     }       @Override     public void surfaceChanged(SurfaceHolder holder, int format, int width,             int height) {       }       @Override     public void surfaceCreated(SurfaceHolder holder) {         if (!hasSurface) {             hasSurface = true;             initCamera(holder);         }       }       @Override     public void surfaceDestroyed(SurfaceHolder holder) {         hasSurface = false;       } 這個surfaceView 創建出來之後,其實也把攝像頭的配置信息以及硬件信息初始化好了。   OK,經過上面一個oncreate以及布局文件的加載,我們已經知道,攝像頭預覽成功,   這個自定義的View又是干嘛的?我們繼續看源碼:   private final int maskColor; private final int resultColor;   private final int resultPointColor; private Collection<ResultPoint> possibleResultPoints; private Collection<ResultPoint> lastPossibleResultPoints;   boolean isFirst;   public ViewfinderView(Context context, AttributeSet attrs) {     super(context, attrs);       density = context.getResources().getDisplayMetrics().density;     //將像素轉換成dp     ScreenRate = (int)(20 * density);       paint = new Paint();     Resources resources = getResources();     maskColor = resources.getColor(R.color.viewfinder_mask);     resultColor = resources.getColor(R.color.result_view);       resultPointColor = resources.getColor(R.color.possible_result_points);     possibleResultPoints = new HashSet<ResultPoint>(5); }   @Override public void onDraw(Canvas canvas) {     //中間的掃描框,你要修改掃描框的大小,去CameraManager裡面修改     Rect frame = CameraManager.get().getFramingRect();     if (frame == null) {         return;     }       //初始化中間線滑動的最上邊和最下邊     if(!isFirst){         isFirst = true;         slideTop = frame.top;         slideBottom = frame.bottom;     }       //獲取屏幕的寬和高     int width = canvas.getWidth();     int height = canvas.getHeight();       paint.setColor(resultBitmap != null ? resultColor : maskColor);       //畫出掃描框外面的陰影部分,共四個部分,掃描框的上面到屏幕上面,掃描框的下面到屏幕下面     //掃描框的左邊面到屏幕左邊,掃描框的右邊到屏幕右邊     canvas.drawRect(0, 0, width, frame.top, paint);     canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);     canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,             paint);     canvas.drawRect(0, frame.bottom + 1, width, height, paint);           if (resultBitmap != null) {         // Draw the opaque result bitmap over the scanning rectangle         paint.setAlpha(OPAQUE);         canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);     } else {           //畫掃描框邊上的角,總共8個部分         paint.setColor(Color.GREEN);         canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,                 frame.top + CORNER_WIDTH, paint);         canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top                 + ScreenRate, paint);         canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,                 frame.top + CORNER_WIDTH, paint);         canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top                 + ScreenRate, paint);         canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left                 + ScreenRate, frame.bottom, paint);         canvas.drawRect(frame.left, frame.bottom - ScreenRate,                 frame.left + CORNER_WIDTH, frame.bottom, paint);         canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,                 frame.right, frame.bottom, paint);         canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,                 frame.right, frame.bottom, paint);             //繪制中間的線,每次刷新界面,中間的線往下移動SPEEN_DISTANCE         slideTop += SPEEN_DISTANCE;         if(slideTop >= frame.bottom){             slideTop = frame.top;         }         canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH/2, frame.right - MIDDLE_LINE_PADDING,slideTop + MIDDLE_LINE_WIDTH/2, paint);             //畫掃描框下面的字         paint.setColor(Color.WHITE);         paint.setTextSize(TEXT_SIZE * density);         paint.setAlpha(0x40);         paint.setTypeface(Typeface.create("System", Typeface.BOLD));         canvas.drawText(getResources().getString(R.string.scan_text), frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density), paint);               Collection<ResultPoint> currentPossible = possibleResultPoints;         Collection<ResultPoint> currentLast = lastPossibleResultPoints;         if (currentPossible.isEmpty()) {             lastPossibleResultPoints = null;         } else {             possibleResultPoints = new HashSet<ResultPoint>(5);             lastPossibleResultPoints = currentPossible;             paint.setAlpha(OPAQUE);             paint.setColor(resultPointColor);             for (ResultPoint point : currentPossible) {                 canvas.drawCircle(frame.left + point.getX(), frame.top                         + point.getY(), 6.0f, paint);             }         }         if (currentLast != null) {             paint.setAlpha(OPAQUE / 2);             paint.setColor(resultPointColor);             for (ResultPoint point : currentLast) {                 canvas.drawCircle(frame.left + point.getX(), frame.top                         + point.getY(), 3.0f, paint);             }         }             //只刷新掃描框的內容,其他地方不刷新         postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,                 frame.right, frame.bottom);       } }   public void drawViewfinder() {     resultBitmap = null;     invalidate(); }   /**  * Draw a bitmap with the result points highlighted instead of the live  * scanning display.  *   * @param barcode  *            An image of the decoded barcode.  */ public void drawResultBitmap(Bitmap barcode) {     resultBitmap = barcode;     invalidate(); }   public void addPossibleResultPoint(ResultPoint point) {     possibleResultPoints.add(point); } 哦,這個就是定義了一個有動態效果的掃描界面 上面的雖然代碼不多,當時我們現在回憶一下步驟:   1,啟動activity,加載布局文件,初始化surfaceView,初始化自定義的View(動態界面),   2,在初始化surfaceView的時候,同時初始化了攝像頭的參數,初始化的handler處理器,啟動了攝像頭預覽。   問題:那什麼時候開始監聽掃描二維碼的呢?   初始化handler 的時候就開始監聽了,看一下其構造函數:   public CaptureActivityHandler(Scaner activity, Vector<BarcodeFormat> decodeFormats,       String characterSet) {     this.activity = activity;     decodeThread = new DecodeThread(activity, decodeFormats, characterSet,         new ViewfinderResultPointCallback(activity.getViewfinderView()));     decodeThread.start();     state = State.SUCCESS;     // Start ourselves capturing previews and decoding.     CameraManager.get().startPreview();     restartPreviewAndDecode();   } 再來一個:上面構造函數new了一個對象,這個對象就是用來監聽獲取掃描的圖像的。   直到獲取了二維碼圖像,調用回調函數就結束。   final class DecodeThread extends Thread {     public static final String BARCODE_BITMAP = "barcode_bitmap";   private final Scaner activity;   private final Hashtable<DecodeHintType, Object> hints;   private Handler handler;   private final CountDownLatch handlerInitLatch;     DecodeThread(Scaner activity,                Vector<BarcodeFormat> decodeFormats,                String characterSet,                ResultPointCallback resultPointCallback) {       this.activity = activity;     handlerInitLatch = new CountDownLatch(1);       hints = new Hashtable<DecodeHintType, Object>(3);       if (decodeFormats == null || decodeFormats.isEmpty()) {          decodeFormats = new Vector<BarcodeFormat>();          decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);          decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);          decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);     }       hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);       if (characterSet != null) {       hints.put(DecodeHintType.CHARACTER_SET, characterSet);     }       hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);   }     Handler getHandler() {     try {       handlerInitLatch.await();     } catch (InterruptedException ie) {       // continue?     }     return handler;   }     @Override   public void run() {     Looper.prepare();     handler = new DecodeHandler(activity, hints);     handlerInitLatch.countDown();     Looper.loop();   }   } 回調函數:   public void handleDecode(Result result, Bitmap barcode) {     inactivityTimer.onActivity();     playBeepSoundAndVibrate();     String resultString = result.getText();     if (resultString.equals("")) {         Toast.makeText(Scaner.this, "Scan failed!",3000).show();     } else {           //掃描結果的處理。     }     Scaner.this.finish(); }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved