Android 二维码相关(三)
本篇文章主要讲下二维码扫描相关.
1: 权限相关
由于文中需要使用Camera以及surfaceview,所以我们需要提前申请相机权限:
java
<uses-permission android:name="android.permission.CAMERA" />
2: 添加SurfaceView预览
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ScanActivity">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:id="@+id/surface"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
3: 初始化相机
通过SurfaceView我们获取SurfaceHolder:
java
surfaceView = findViewById(R.id.surface);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
- surfaceCreated中我们初始化相机配置
- surfaceDestroyed 中我们重置参数
3.1 startCamera()
java
private void startCamera() {
Camera open = Camera.open();
Camera.Parameters parameters = open.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
open.setParameters(parameters);
open.setDisplayOrientation(90);
open.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
});
try {
open.setPreviewDisplay(surfaceView.getHolder());
open.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
3.2 stopCamera()
java
private void stopCamera() {
if (open != null) {
open.stopPreview();
open.setPreviewCallback(null);
open.release();
open = null;
}
}
3.3 Camera.PreviewCallback
具体的解析方法,以及数据来源根据Camera.PreviewCallback 的回调来实现:
-
获取数据的宽高
javaCamera.Size previewSize = camera.getParameters().getPreviewSize(); int width = previewSize.width; int height = previewSize.height;
-
将预览数据转换为二进制位图
javaPlanarYUVLuminanceSource planarYUVLuminanceSource = new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false); BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(planarYUVLuminanceSource));
-
设置解码参数
javaEnumMap<DecodeHintType, Object> decodeHintTypeObjectEnumMap = new EnumMap<>(DecodeHintType.class); Collection<BarcodeFormat> list = new ArrayList<>(); list.addAll(EnumSet.of(BarcodeFormat.QR_CODE)); decodeHintTypeObjectEnumMap.put(DecodeHintType.POSSIBLE_FORMATS, list);
-
解码
创建MultiFormatReader对象,用于解码.并通过setHints方法设置解码格式
javaMultiFormatReader multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(decodeHintTypeObjectEnumMap); try { Result decode = multiFormatReader.decode(binaryBitmap); String text = decode.getText(); Log.i("xxxxx", "onPreviewFrame: " + text); } catch (Throwable e) { Log.e("xxxxx", "onPreviewFrame: ", e); }
注: onPreviewFrame是Android相机预览帧回调函数,它会在每一帧预览数据可用时被调用.
完整的代码如下:
java
public class ScanActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PreviewCallback {
private SurfaceView surfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
surfaceView = findViewById(R.id.surface);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(this);
}
Camera open;
private void startCamera() {
Camera open = Camera.open();
Camera.Parameters parameters = open.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
open.setParameters(parameters);
open.setDisplayOrientation(90);
open.setPreviewCallback(this);
try {
open.setPreviewDisplay(surfaceView.getHolder());
open.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
private void stopCamera() {
if (open != null) {
open.stopPreview();
open.setPreviewCallback(null);
open.release();
open = null;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
startCamera();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopCamera();
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (camera == null || camera.getParameters() == null) return;
Camera.Size previewSize = camera.getParameters().getPreviewSize();
int width = previewSize.width;
int height = previewSize.height;
//预览数据转二进制位图
PlanarYUVLuminanceSource planarYUVLuminanceSource = new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(planarYUVLuminanceSource));
// 设置解码参数
EnumMap<DecodeHintType, Object> decodeHintTypeObjectEnumMap = new EnumMap<>(DecodeHintType.class);
Collection<BarcodeFormat> list = new ArrayList<>();
list.addAll(EnumSet.of(BarcodeFormat.QR_CODE));
decodeHintTypeObjectEnumMap.put(DecodeHintType.POSSIBLE_FORMATS, list);
MultiFormatReader multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(decodeHintTypeObjectEnumMap);
try {
Result decode = multiFormatReader.decode(binaryBitmap);
String text = decode.getText();
Log.i("xxxxx", "onPreviewFrame: " + text);
} catch (Throwable e) {
Log.e("xxxxx", "onPreviewFrame: ", e);
}
}
}