上篇文章,我们大概了解了zxing扫描的整个流程,现在我们就仿照Google爸爸的例子,来自己做一个扫码程序。开启疯狂模(chao)仿(xi)模式。
Android studio新建一个工程或者一个module。从Google Demo工程里,复制过来一些关键类。
首先在build.gradle里加入zxing的依赖
dependencies {
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.google.zxing:android-core:3.3.0'
}
将camera包的所有类复制到自己的工程
image.png
此时工程会报一些错,一些是包名修改导致import 不对,重新引入即可,还有一些是获取一些设置的类没有找到,这个我们全都默认改成True 或者false 的默认值 就好了,自己可以做标记,后期再做处理。
image.png
把红色错误搞定后,再将下面这些类也复制过来。
AmbientLightManager
BeepManager
CaptureActivity
CaptureActivityHandler
DecodeHandler
DecodeThread
InactivityTimer
ViewfinderResultPointCallback
DecodeFormatManager
ViewfinderView
Intents
看到一堆错误 是不是很慌,没关系,我们一点点来改。AndroidManifest.xml 加入各种相机权限,直接从例子中的文件复制过来即可,color,ids资源文件复制
6.0系统,代码权限申请,很重要,要不相机打不开 只能看到一片黑
<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 错误导入各种类就好了,由于我们只做扫描功能,页面的布局文件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 相关均可删除,captureActivity类基本只需保留initCamera和 handledecode()两个方法 handleDecode 里代码基本也可以删除,以下为删除后的代码,可直接复制。这里没有加 申请权限的的代码,直接在应用权限管理打开的。
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 中设置camera 这个很重要
handler = null;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
beepManager.updatePrefs(); //手机震动
ambientLightManager.start(cameraManager); //闪关灯
inactivityTimer.onResume(); //检查手机电量,低电量关闭扫码
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 {
//打开相机
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (handler == null) {
//将相机等参数在此传给CaptureActivityHandler captureActivityHandler 处理后会 回调CaptureActivity的
// handleDecode()方法 handleDecode方法得到 解析结果 然后做自己的处理
//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);
}
基本就大功告成了,其他类都是一些导入错误或者默认值,稍作修改即可。
运行程序,我们最简单版,最糙版的扫码程序就做好了
image.png
下一篇,将进行优化讲解,将该程序打造成一个用户可用,稍微好看一点的程序。
(通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(三))[https://www.jianshu.com/p/c91ab876ada1]
网友评论