做公司項目的時候需要實現類似於超市掃描槍一樣的,連續掃描。下面說說我的實現方法。
既然要修改Zxing的源碼,就要找到Zxing裏面的CaptureActivity這個類,這裏面內容是點擊掃描按鈕跳轉到掃描界面和一些掃描完以後獲取條形碼(二維碼)數據的方法。大部分大同小異,我把這個類貼出來 :
public final class CaptureActivity extends Activity implements
SurfaceHolder.Callback {
private static final String TAG = CaptureActivity.class.getSimpleName();
private CameraManager cameraManager;
private CaptureActivityHandler handler;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private SurfaceView scanPreview = null;
private RelativeLayout scanContainer;
private RelativeLayout scanCropView;
private ImageView scanLine;
private Rect mCropRect = null;
public Handler getHandler() {
return handler;
}
public CameraManager getCameraManager() {
return cameraManager;
}
private boolean isHasSurface = false;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_capture);
scanPreview = (SurfaceView) findViewById(R.id.capture_preview);
scanContainer = (RelativeLayout) findViewById(R.id.capture_container);
scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view);
scanLine = (ImageView) findViewById(R.id.capture_scan_line);
inactivityTimer = new InactivityTimer(this);
beepManager = new BeepManager(this);
TranslateAnimation animation = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.9f);
animation.setDuration(4500);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.RESTART);
scanLine.startAnimation(animation);
}
@Override
protected void onResume() {
super.onResume();
// CameraManager must be initialized here, not in onCreate(). This is
// necessary because we don't
// want to open the camera driver and measure the screen size if we're
// going to show the help on
// first launch. That led to bugs where the scanning rectangle was the
// wrong size and partially
// off screen.
cameraManager = new CameraManager(getApplication());
handler = null;
if (isHasSurface) {
// The activity was paused but not stopped, so the surface still
// exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(scanPreview.getHolder());
} else {
// Install the callback and wait for surfaceCreated() to init the
// camera.
scanPreview.getHolder().addCallback(this);
}
inactivityTimer.onResume();
}
@Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
beepManager.close();
cameraManager.closeDriver();
if (!isHasSurface) {
scanPreview.getHolder().removeCallback(this);
}
super.onPause();
}
@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG,
"*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!isHasSurface) {
isHasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isHasSurface = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
/**
* A valid barcode has been found, so give an indication of success and show
* the results.
*
* @param rawResult
* The contents of the barcode.
*
* @param bundle
* The extras
*/
public void handleDecode(Result rawResult, Bundle bundle) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();
if (rawResult != null && !rawResult.getText().equalsIgnoreCase("")) {
Intent intent = new Intent();
intent.putExtra("scan_result", rawResult.getText());
setResult(RESULT_OK, intent);
} else {
setResult(RESULT_CANCELED);
}
CaptureActivity.this.finish();
}
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG,
"initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a
// RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, cameraManager,
DecodeThread.ALL_MODE);
}
initCrop();
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessageAndExit();
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessageAndExit();
}
}
private void displayFrameworkBugMessageAndExit() {
// camera error
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.app_name));
builder.setMessage("相機打開出錯,請稍後重試");
builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
builder.show();
}
public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
}
}
public Rect getCropRect() {
return mCropRect;
}
/**
* 初始化截取的矩形區域
*/
private void initCrop() {
int cameraWidth = cameraManager.getCameraResolution().y;
int cameraHeight = cameraManager.getCameraResolution().x;
/** 獲取佈局中掃描框的位置信息 */
int[] location = new int[2];
scanCropView.getLocationInWindow(location);
int cropLeft = location[0];
int cropTop = location[1] - getStatusBarHeight();
int cropWidth = scanCropView.getWidth();
int cropHeight = scanCropView.getHeight();
/** 獲取佈局容器的寬高 */
int containerWidth = scanContainer.getWidth();
int containerHeight = scanContainer.getHeight();
/** 計算最終截取的矩形的左上角頂點x座標 */
int x = cropLeft * cameraWidth / containerWidth;
/** 計算最終截取的矩形的左上角頂點y座標 */
int y = cropTop * cameraHeight / containerHeight;
/** 計算最終截取的矩形的寬度 */
int width = cropWidth * cameraWidth / containerWidth;
/** 計算最終截取的矩形的高度 */
int height = cropHeight * cameraHeight / containerHeight;
/** 生成最終的截取的矩形 */
mCropRect = new Rect(x, y, width + x, height + y);
}
private int getStatusBarHeight() {
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("status_bar_height");
int x = Integer.parseInt(field.get(obj).toString());
return getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
纔開始實現的時候用的下面這個方法。在handleDecode()裏面調用,然後在CaptureActivityHandler這個類裏面把restartPreviewAndDecode這個方法private 改成public。發現也是可以用的,但是速度太快,操作起來不怎麼方便。
private void continuePreview() {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.capture_preview);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
initCamera(surfaceHolder);
if (handler != null) {
handler.restartPreviewAndDecode();
}
}
最後,最後,最後自己直接用定時器來實現,很完美了。在handleDecode()這個方法裏面,獲取到數據以後直接添上這句代碼 mHandler.postDelayed(runnable, 3000); 。下面是方法。
Handler mHandler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
// handler自帶方法實現定時器
try {
if (handler != null)
mHandler.postDelayed(runnable, 3000);
handler.restartPreviewAndDecode(); // 實現多次掃描
System.out.println("do...");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("exception...");
}
}
};