你的位置:首页 > 操作系统

[操作系统]自定义ZXing二维码扫描界面并解决取景框拉伸等问题

先看效果

 

扫描内容是下面这张,二维码是用zxing库生成的

 

由于改了好几个类,还是去年的事都忘得差不多了,所以只能上这个类的代码了,主要就是改了这个CaptureActivity.java

 

 1 package com.zxing.activity; 2  3 import java.io.IOException; 4 import java.util.Vector; 5  6 import android.app.Activity; 7 import android.content.Intent; 8 import android.content.res.AssetFileDescriptor; 9 import android.graphics.Bitmap; 10 import android.media.AudioManager; 11 import android.media.MediaPlayer; 12 import android.media.MediaPlayer.OnCompletionListener; 13 import android.os.Handler; 14 import android.os.Vibrator; 15 import android.view.SurfaceHolder; 16 import android.view.SurfaceHolder.Callback; 17 import android.view.SurfaceView; 18 import android.widget.Toast; 19  20 import com.ericssonlabs.R; 21 import com.google.zxing.BarcodeFormat; 22 import com.google.zxing.Result; 23 import com.zxing.camera.CameraManager; 24 import com.zxing.decoding.CaptureActivityHandler; 25 import com.zxing.decoding.CaptureActivityHandler.DecodeCallback; 26 import com.zxing.decoding.InactivityTimer; 27 import com.zxing.view.ViewfinderView; 28 /** 29  * Initial the camera 30  * @author Ryan.Tang 31  * @modifier Lemon 32  * @use extends CaptureActivity并且在setContentView方法后调用init方法 33 */ 34 public abstract class CaptureActivity extends Activity implements Callback, DecodeCallback { 35   //  private static final String TAG = "CaptureActivity"; 36  37   protected Activity context; 38   protected SurfaceView surfaceView; 39   protected ViewfinderView viewfinderView; 40   /**初始化,必须在setContentView之后 41    * @param context 42    * @param viewfinderView 43   */ 44   protected void init(Activity context, SurfaceView surfaceView, ViewfinderView viewfinderView) { 45     this.context = context; 46     this.surfaceView = surfaceView; 47     this.viewfinderView = viewfinderView; 48  49     CameraManager.init(getApplication()); 50  51     hasSurface = false; 52     inactivityTimer = new InactivityTimer(this); 53   } 54  55  56   private CaptureActivityHandler handler; 57   private boolean hasSurface; 58   private Vector<BarcodeFormat> decodeFormats; 59   private String characterSet; 60   private InactivityTimer inactivityTimer; 61   private MediaPlayer mediaPlayer; 62   private boolean playBeep; 63   private static final float BEEP_VOLUME = 0.10f; 64   private boolean vibrate; 65  66  67   @Override 68   protected void onResume() { 69     super.onResume(); 70     SurfaceHolder surfaceHolder = surfaceView.getHolder(); 71     if (hasSurface) { 72       initCamera(surfaceHolder); 73     } else { 74       surfaceHolder.addCallback(this); 75       surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 76     } 77     decodeFormats = null; 78     characterSet = null; 79  80     playBeep = true; 81     AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE); 82     if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { 83       playBeep = false; 84     } 85     initBeepSound(); 86     vibrate = true; 87   } 88  89   @Override 90   protected void onPause() { 91     super.onPause(); 92     if (handler != null) { 93       handler.quitSynchronously(); 94       handler = null; 95     } 96     CameraManager.get().closeDriver(); 97   } 98  99   @Override100   protected void onDestroy() {101     inactivityTimer.shutdown();102     super.onDestroy();103   }104 105 106   public static final String RESULT_QRCODE_STRING = "RESULT_QRCODE_STRING";107   /**108    * Handler scan result109    * @param result110    * @param barcode111   */112   public void handleDecode(Result result, Bitmap barcode) {113     inactivityTimer.onActivity();114     playBeepSoundAndVibrate();115     String resultString = result.getText();116     //FIXME117     if (resultString.equals("")) {118       Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();119     }120 121     setResult(RESULT_OK, new Intent().putExtra(RESULT_QRCODE_STRING, resultString));122     finish();123   }124 125   private void initCamera(SurfaceHolder surfaceHolder) {126     try {127       CameraManager.get().openDriver(surfaceHolder);128     } catch (IOException ioe) {129       return;130     } catch (RuntimeException e) {131       return;132     }133     if (handler == null) {134       handler = new CaptureActivityHandler(this, decodeFormats,135           characterSet, viewfinderView, this);136     }137   }138 139   @Override140   public void drawViewfinder() {141     viewfinderView.drawViewfinder();    142   }143 144 145   @Override146   public void surfaceChanged(SurfaceHolder holder, int format, int width,147       int height) {148 149   }150 151   @Override152   public void surfaceCreated(SurfaceHolder holder) {153     if (!hasSurface) {154       hasSurface = true;155       initCamera(holder);156     }157 158   }159 160   @Override161   public void surfaceDestroyed(SurfaceHolder holder) {162     hasSurface = false;163 164   }165 166 167   public Handler getHandler() {168     return handler;169   }170 171 172   private void initBeepSound() {173     if (playBeep && mediaPlayer == null) {174       // The volume on STREAM_SYSTEM is not adjustable, and users found it175       // too loud,176       // so we now play on the music stream.177       setVolumeControlStream(AudioManager.STREAM_MUSIC);178       mediaPlayer = new MediaPlayer();179       mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);180       mediaPlayer.setOnCompletionListener(beepListener);181 182       AssetFileDescriptor file = getResources().openRawResourceFd(183           R.raw.beep);184       try {185         mediaPlayer.setDataSource(file.getFileDescriptor(),186             file.getStartOffset(), file.getLength());187         file.close();188         mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);189         mediaPlayer.prepare();190       } catch (IOException e) {191         mediaPlayer = null;192       }193     }194   }195 196   private static final long VIBRATE_DURATION = 200L;197 198   private void playBeepSoundAndVibrate() {199     if (playBeep && mediaPlayer != null) {200       mediaPlayer.start();201     }202     if (vibrate) {203       Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);204       vibrator.vibrate(VIBRATE_DURATION);205     }206   }207 208   /**209    * When the beep has finished playing, rewind to queue up another one.210   */211   private final OnCompletionListener beepListener = new OnCompletionListener() {212     public void onCompletion(MediaPlayer mediaPlayer) {213       mediaPlayer.seekTo(0);214     }215   };216 217 }

使用方法: 新建一个Activity继承CaptureActivity并且在setContentView方法后调用init方法即可。
示例:

CameraScanActivity.java

 1 package zuo.biao.activity; 2  3 import zuo.biao.R; 4 import zuo.biao.library.interfaces.OnBottomDragListener; 5 import zuo.biao.util.ActivityUtil; 6 import android.content.Context; 7 import android.content.Intent; 8 import android.os.Bundle; 9 import android.view.SurfaceHolder.Callback; 10 import android.view.SurfaceView; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13  14 import com.zxing.activity.CaptureActivity; 15 import com.zxing.camera.CameraManager; 16 import com.zxing.view.ViewfinderView; 17  18 /**扫描二维码Activity 19  * @author Lemon 20  * @use 参考zuo.biao.library.ModelActivity 21 */ 22 public class CameraScanActivity extends CaptureActivity implements Callback, OnClickListener, OnBottomDragListener { 23   public static final String TAG = "CameraScanActivity"; 24  25   //启动方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 26  27   /**启动这个Activity的Intent 28    * @param context 29    * @param title 30    * @return 31   */ 32   public static Intent createIntent(Context context) { 33     return new Intent(context, CameraScanActivity.class); 34   } 35  36   //启动方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 37  38  39  40   @Override 41   protected void onCreate(Bundle savedInstanceState) { 42     super.onCreate(savedInstanceState); 43     setContentView(R.layout.camera_scan_activity); 44     init(this, (SurfaceView) findViewById(R.id.svCameraScan), (ViewfinderView) findViewById(R.id.vfvCameraScan)); 45  46     //功能归类分区方法,必须调用<<<<<<<<<< 47     initView(); 48     initData(); 49     initListener(); 50     //功能归类分区方法,必须调用>>>>>>>>>> 51  52   } 53  54  55   //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 56  57   public void initView() {//必须调用 58  59   } 60  61  62  63   //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 64  65  66  67  68  69  70  71  72  73  74   //data数据区(存在数据获取或处理代码,但不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 75  76   public void initData() {//必须调用 77  78   } 79  80  81   //data数据区(存在数据获取或处理代码,但不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 82  83  84  85  86  87  88  89  90   //listener事件监听区(只要存在事件监听代码就是)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 91  92   public void initListener() {//必须调用 93  94     findViewById(R.id.tvCameraScanReturn).setOnClickListener(this); 95     findViewById(R.id.ivCameraScanReturn).setOnClickListener(this); 96     findViewById(R.id.ivCameraScanLight).setOnClickListener(this); 97     findViewById(R.id.ivCameraScanMyQRCode).setOnClickListener(this); 98   } 99 100   //系统自带监听方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<101 102   @Override103   public void onDragBottom(boolean rightToLeft) {104     if (rightToLeft) {105 106       return;107     }108 109     finish();110   }111 112   @Override113   public void onClick(View v) {114     switch (v.getId()) {115     case R.id.tvCameraScanReturn:116     case R.id.ivCameraScanReturn:117       onDragBottom(false);118       break;119     case R.id.ivCameraScanLight:120       switchLight(! isOpen);121       break;122     case R.id.ivCameraScanMyQRCode:123       //124       break;125     default:126       break;127     }128   }129 130 131   private boolean isOpen = false;132   /**打开或关闭闪关灯133    * @param open134   */135   private void switchLight(boolean open) {136     if (open == isOpen) {137       return;138     }139     isOpen = CameraManager.get().switchLight(open);140   }141 142 143   //类相关监听<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<144 145 146   //类相关监听>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>147 148   //系统自带监听方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>149 150 151   //listener事件监听区(只要存在事件监听代码就是)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>152 153 154 155 156 157 158 159 160   //内部类,尽量少用<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<161 162 163 164   //内部类,尽量少用>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>165 166 }

 

CameraScanActivity布局文件camera_scan_activity.

 1 <??> 2 <FrameLayout ="http://schemas.android.com/apk/res/android" 3   style="@style/activity_page" > 4  5   <SurfaceView 6     android:id="@+id/svCameraScan" 7     android:layout_width="wrap_content" 8     android:layout_height="wrap_content" 9     android:layout_gravity="center" /> 10  11   <!-- 必须在最底层,且不能指定宽高,否则扫描读取很难实现 --> 12   <com.zxing.view.ViewfinderView 13     android:id="@+id/vfvCameraScan" 14     android:layout_width="match_parent" 15     android:layout_height="match_parent" /> 16  17   <LinearLayout 18     style="@style/ll_vertical_match_match" 19     android:baselineAligned="false" > 20  21     <RelativeLayout 22       style="@style/topbar_bg" 23       android:background="@color/white_alpha" > 24  25       <TextView 26         android:id="@+id/tvCameraScanReturn" 27         style="@style/topbar_left_btn" 28         android:text="     " /> 29  30       <TextView 31         style="@style/topbar_title" 32         android:layout_centerHorizontal="true" 33         android:text="扫一扫" /> 34     </RelativeLayout> 35  36     <LinearLayout 37       style="@style/ll_vertical_match_match" 38       android:layout_gravity="center_horizontal" 39       android:layout_weight="1" > 40  41       <TextView 42         style="@style/text_middle_white" 43         android:layout_margin="30dp" 44         android:text="@string/camera_scan_remind" /> 45     </LinearLayout> 46  47     <LinearLayout 48       style="@style/ll_horizontal_match_wrap" 49       android:layout_gravity="bottom" 50       android:background="@color/white_alpha" 51       android:gravity="center" > 52  53       <LinearLayout 54         style="@style/ll_vertical_wrap_wrap" 55         android:layout_margin="12dp" 56         android:paddingBottom="4dp" 57         android:paddingLeft="12dp" 58         android:paddingRight="12dp" 59         android:paddingTop="4dp" > 60  61         <ImageView 62           android:id="@+id/ivCameraScanReturn" 63           style="@style/wrap_wrap" 64           android:background="@drawable/cilcle_gray_to_white" 65           android:padding="12dp" 66           android:src="@drawable/back_black_light" /> 67  68         <TextView 69           style="@style/text_small" 70           android:layout_marginTop="4dp" 71           android:text="返回" /> 72       </LinearLayout> 73  74       <LinearLayout 75         style="@style/ll_vertical_wrap_wrap" 76         android:layout_margin="12dp" 77         android:paddingBottom="4dp" 78         android:paddingLeft="12dp" 79         android:paddingRight="12dp" 80         android:paddingTop="4dp" > 81  82         <ImageView 83           android:id="@+id/ivCameraScanLight" 84           style="@style/wrap_wrap" 85           android:background="@drawable/cilcle_gray_to_white" 86           android:padding="12dp" 87           android:src="@drawable/flash_light" /> 88  89         <TextView 90           style="@style/text_small" 91           android:layout_marginTop="4dp" 92           android:text="开灯/关灯" /> 93       </LinearLayout> 94  95       <LinearLayout 96         style="@style/ll_vertical_wrap_wrap" 97         android:layout_margin="12dp" 98         android:paddingBottom="4dp" 99         android:paddingLeft="12dp"100         android:paddingRight="12dp"101         android:paddingTop="4dp" >102 103         <ImageView104           android:id="@+id/ivCameraScanMyQRCode"105           style="@style/wrap_wrap"106           android:background="@drawable/cilcle_gray_to_white"107           android:padding="12dp"108           android:src="@drawable/qrcode" />109 110         <TextView111           style="@style/text_small"112           android:layout_marginTop="4dp"113           android:text="我的名片" />114       </LinearLayout>115     </LinearLayout>116   </LinearLayout>117 118 </FrameLayout>

布局文件因为使用了ZBLibrary中的一些style,color等,只有这个layout的话会出现一些错误。自己新建一个layout文件并把ViewfinderView放到最外层布局文件内就行了。当然下载好下面附上的源码就没这问题了。

对了,记得在AndroidManifest.

    <uses-permission android:name="android.permission.CAMERA" />

    <uses-feature android:name="android.hardware.camera" />

    <uses-feature android:name="android.hardware.camera.autofocus" />

    <uses-permission android:name="android.permission.FLASHLIGHT" />

 

附源码(含使用方法)

 

GitHub源码      https://github.com/TommyLemon/Android-ZBLibrary 

开源中国源码    http://git.oschina.net/Lemon19950301/Android-ZBLibrary