上篇文章,我們大概了解了zxing掃描的整個流程,現(xiàn)在我們就仿照Google爸爸的例子,來自己做一個掃碼程序。開啟瘋狂模(chao)仿(xi)模式。
Android studio新建一個工程或者一個module。從Google Demo工程里,復(fù)制過來一些關(guān)鍵類。
首先在build.gradle里加入zxing的依賴
dependencies {
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.google.zxing:android-core:3.3.0'
}
將camera包的所有類復(fù)制到自己的工程

此時工程會報一些錯,一些是包名修改導(dǎo)致import 不對,重新引入即可,還有一些是獲取一些設(shè)置的類沒有找到,這個我們?nèi)寄J(rèn)改成True 或者false 的默認(rèn)值 就好了,自己可以做標(biāo)記,后期再做處理。

把紅色錯誤搞定后,再將下面這些類也復(fù)制過來。
AmbientLightManager
BeepManager
CaptureActivity
CaptureActivityHandler
DecodeHandler
DecodeThread
InactivityTimer
ViewfinderResultPointCallback
DecodeFormatManager
ViewfinderView
Intents
看到一堆錯誤 是不是很慌,沒關(guān)系,我們一點(diǎn)點(diǎn)來改。AndroidManifest.xml 加入各種相機(jī)權(quán)限,直接從例子中的文件復(fù)制過來即可,color,ids資源文件復(fù)制
6.0系統(tǒng),代碼權(quán)限申請,很重要,要不相機(jī)打不開 只能看到一片黑
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-feature android:name="android.hardware.camera.any"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
<uses-feature android:name="android.hardware.screen.landscape"/>
<uses-feature android:name="android.hardware.wifi" android:required="false"/>
CaptureActivity.class
import 錯誤導(dǎo)入各種類就好了,由于我們只做掃描功能,頁面的布局文件layout就可以很簡單了
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<SurfaceView android:id="@+id/preview_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<com.jian.zxingdemo.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</merge>
刪除無用類和代碼,onResume中的getIntent 相關(guān)均可刪除,captureActivity類基本只需保留initCamera和 handledecode()兩個方法 handleDecode 里代碼基本也可以刪除,以下為刪除后的代碼,可直接復(fù)制。這里沒有加 申請權(quán)限的的代碼,直接在應(yīng)用權(quán)限管理打開的。
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_capture);
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
beepManager = new BeepManager(this);
ambientLightManager = new AmbientLightManager(this);
}
@Override
protected void onResume() {
super.onResume();
// historyManager must be initialized here to update the history preference
// 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());
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
viewfinderView.setCameraManager(cameraManager); //viewfinderView 中設(shè)置camera 這個很重要
handler = null;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
beepManager.updatePrefs(); //手機(jī)震動
ambientLightManager.start(cameraManager); //閃關(guān)燈
inactivityTimer.onResume(); //檢查手機(jī)電量,低電量關(guān)閉掃碼
decodeFormats = null;
characterSet = null;
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
// The activity was paused but not stopped, so the surface still exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(surfaceHolder);
} else {
// Install the callback and wait for surfaceCreated() to init the camera.
surfaceHolder.addCallback(this);
}
}
private int getCurrentOrientation() {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
switch (rotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_90:
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
default:
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
}
} else {
switch (rotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_270:
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
default:
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
}
}
@Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
ambientLightManager.stop();
beepManager.close();
cameraManager.closeDriver();
//historyManager = null; // Keep for onActivityResult
if (!hasSurface) {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.removeCallback(this);
}
super.onPause();
}
@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
cameraManager.setTorch(false);
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
cameraManager.setTorch(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
// Bitmap isn't used yet -- will be used soon
if (handler == null) {
savedResultToShow = result;
} else {
if (result != null) {
savedResultToShow = result;
}
if (savedResultToShow != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
handler.sendMessage(message);
}
savedResultToShow = null;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// do nothing
}
/**
* A valid barcode has been found, so give an indication of success and show the results.
*
* @param rawResult The contents of the barcode.
* @param scaleFactor amount by which thumbnail was scaled
* @param barcode A greyscale bitmap of the camera data which was decoded.
*/
public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();
//
Toast.makeText(this,rawResult.getText(),Toast.LENGTH_SHORT).show();
restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
}
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 {
//打開相機(jī)
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (handler == null) {
//將相機(jī)等參數(shù)在此傳給CaptureActivityHandler captureActivityHandler 處理后會 回調(diào)CaptureActivity的
// handleDecode()方法 handleDecode方法得到 解析結(jié)果 然后做自己的處理
//decodeFomats:支持掃碼的類型
handler = new CaptureActivityHandler(this, decodeFormats, null, characterSet, cameraManager);
}
decodeOrStoreSavedBitmap(null, null);
} 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();
}
}
public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
}
}
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
}
在decodeThread 類里,
// The prefs can't change while the thread is running, so pick them up once here.
if (decodeFormats == null || decodeFormats.isEmpty()) {
decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
}
基本就大功告成了,其他類都是一些導(dǎo)入錯誤或者默認(rèn)值,稍作修改即可。
運(yùn)行程序,我們最簡單版,最糙版的掃碼程序就做好了

下一篇,將進(jìn)行優(yōu)化講解,將該程序打造成一個用戶可用,稍微好看一點(diǎn)的程序。
(通過谷歌官方例子手把手教你改造自己的Zxing掃描android程序(三))[http://m.itdecent.cn/p/c91ab876ada1]