Androidでバーコードを読み込む
ふと思い立ちAndroidにてバーコードを読み込めないかと調べてみる
ネットで検索するとZXingライブラリというのを使う方法と、Google Playサービスを使う方法が紹介されていた
今回はGoogle Playサービスを使用しバーコードを読み込んでみた
よく紹介されている方法はバーコード読み取り用のActivityを別に立ち上げ、その結果を取得する方法である
しかしここでは同じActivity内でカメラを使用しバーコードを取得、表示する
あくまで簡易的に処理、表示するためのものなのであしからず
まずは外部ライブラリを使用するのでgradleを編集する
dependenciesに以下を追加
AndroidManifest.xmlにカメラ権限を追加
Activityでカメラ権限を要求
クラスを作成しFrameLayoutを継承する
これはレイアウト下にカメラの映像を表示するTextureView
そしてオーバーレイでバーコードの矩形と番号を表示するViewを重ねて表示するため
以下関係ない場所は省略してあるので注意
Barcodeに必要な情報は入っているので後は好き好きだが、例としてOverlayのコードも書いておく
ネットで検索するとZXingライブラリというのを使う方法と、Google Playサービスを使う方法が紹介されていた
今回はGoogle Playサービスを使用しバーコードを読み込んでみた
よく紹介されている方法はバーコード読み取り用のActivityを別に立ち上げ、その結果を取得する方法である
しかしここでは同じActivity内でカメラを使用しバーコードを取得、表示する
あくまで簡易的に処理、表示するためのものなのであしからず
まずは外部ライブラリを使用するのでgradleを編集する
dependenciesに以下を追加
implementation 'com.google.android.gms:play-services-vision:20.1.3'
これはバーコードのためだけに入れているので、すべてのPlayサービスを読み込むplay-servicesが入っていれば追加する必要はないAndroidManifest.xmlにカメラ権限を追加
<uses-permission android:name="android.permission.CAMERA" />
Activityでカメラ権限を要求
private class requestPermission() {
final int REQUEST_CODE_CAMERA_PERMISSION = 0x01;
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_CAMERA_PERMISSION);
}
}
final int REQUEST_CODE_CAMERA_PERMISSION = 0x01;
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_CAMERA_PERMISSION);
}
}
クラスを作成しFrameLayoutを継承する
これはレイアウト下にカメラの映像を表示するTextureView
そしてオーバーレイでバーコードの矩形と番号を表示するViewを重ねて表示するため
以下関係ない場所は省略してあるので注意
public class BarcodePreview extends FrameLayout {
CameraManager cameraManager;
CameraDevice cameraDevice;
ImageReader reader;
TextureView textureView;
BarcodeOverlay overlay;
BarcodeDetector detector;
int[] capSize = {1920,1080};
public BarcodePreview(Context context) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
reader = ImageReader.newInstance(capSize[0],capSize[1], ImageFormat.JPEG,1);
textureView = new TextureView(context);
overlay = new BarcodeOverlay(context);
detector = new BarcodeDetector.Builder(context).build();
//FrameLayoutに2つ追加
addView(textureView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(overlay, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
public void setCameraPreviewState(boolean isEnabled) {
if (isEnabled && cameraDevice == null) {
try {
cameraManager.openCamera(cameraId, cameraCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
} else if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
private final CameraDevice.StateCallback cameraCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
cameraDevice.close();
cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int i) {
cameraDevice.close();
cameraDevice = null;
}
};
private void createCameraPreview() {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(capSize[0], capSize[1]);
Surface surface = new Surface(texture);
Surface readerSurface = reader.getSurface();
try {
List<Surface> surfaceList = Arrays.asList(surface,readerSurface);
previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewBuilder.addTarget(surface);
previewBuilder.set(CaptureRequest.JPEG_ORIENTATION, 90);
cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
captureSession = cameraCaptureSession;
try {
captureSession.setRepeatingRequest(previewBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (Exception e) {}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
int gWidth = 0;
int gHeight = 0;
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
gWidth = width;
gHeight = height;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
new Thread(new Runnable() { //別スレッドに分けないと遅くなる
@Override
public void run() {
Bitmap bitmap = textureView.getBitmap(gWidth,gHeight);
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray<Barcode> barcodes = detector.detect(frame); //これが解析
overlay.setBarcodes(barcodes); //これでOverlayにバーコード情報を送る。これはそのままだが必要な情報だけ送ってもいい
if (barcodes.size() >= 1) {
//バーコードを認識し即処理に移るならbarcodes.valueAt(0)で受け取れる
}
}
}).start();
}
}
}
CameraManager cameraManager;
CameraDevice cameraDevice;
ImageReader reader;
TextureView textureView;
BarcodeOverlay overlay;
BarcodeDetector detector;
int[] capSize = {1920,1080};
public BarcodePreview(Context context) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
reader = ImageReader.newInstance(capSize[0],capSize[1], ImageFormat.JPEG,1);
textureView = new TextureView(context);
overlay = new BarcodeOverlay(context);
detector = new BarcodeDetector.Builder(context).build();
//FrameLayoutに2つ追加
addView(textureView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(overlay, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
public void setCameraPreviewState(boolean isEnabled) {
if (isEnabled && cameraDevice == null) {
try {
cameraManager.openCamera(cameraId, cameraCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
} else if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
private final CameraDevice.StateCallback cameraCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
cameraDevice.close();
cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int i) {
cameraDevice.close();
cameraDevice = null;
}
};
private void createCameraPreview() {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(capSize[0], capSize[1]);
Surface surface = new Surface(texture);
Surface readerSurface = reader.getSurface();
try {
List<Surface> surfaceList = Arrays.asList(surface,readerSurface);
previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewBuilder.addTarget(surface);
previewBuilder.set(CaptureRequest.JPEG_ORIENTATION, 90);
cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
captureSession = cameraCaptureSession;
try {
captureSession.setRepeatingRequest(previewBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (Exception e) {}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
int gWidth = 0;
int gHeight = 0;
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
gWidth = width;
gHeight = height;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
new Thread(new Runnable() { //別スレッドに分けないと遅くなる
@Override
public void run() {
Bitmap bitmap = textureView.getBitmap(gWidth,gHeight);
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray<Barcode> barcodes = detector.detect(frame); //これが解析
overlay.setBarcodes(barcodes); //これでOverlayにバーコード情報を送る。これはそのままだが必要な情報だけ送ってもいい
if (barcodes.size() >= 1) {
//バーコードを認識し即処理に移るならbarcodes.valueAt(0)で受け取れる
}
}
}).start();
}
}
}
Barcodeに必要な情報は入っているので後は好き好きだが、例としてOverlayのコードも書いておく
class BarcodeOverlay extends View {
private SparseArray<Barcode> barcodes;
public void setBarcodes(SparseArray<Barcode> barcodes) {
this.barcodes = barcodes;
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
Paint painter = new Paint();
for (int i = 0; i < barcodes.size(); i++) {
Barcode barcode = barcodes.valueAt(i);
Point[] p = barcode.cornerPoints; //これに四隅の情報が入る。左上から時計回り
Rect r = new Rect(p[0].x,p[0].y,p[2].x,p[2].y);
painter.setStyle(Paint.Style.STROKE);
canvas.drawRect(r,painter); //枠の描画
painter.setStyle(Paint.Style.FILL);
canvas.drawText(barcode.rawValue,p[0].x,p[2].y,painter); //バーコードナンバーの描画
}
}
}
private SparseArray<Barcode> barcodes;
public void setBarcodes(SparseArray<Barcode> barcodes) {
this.barcodes = barcodes;
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
Paint painter = new Paint();
for (int i = 0; i < barcodes.size(); i++) {
Barcode barcode = barcodes.valueAt(i);
Point[] p = barcode.cornerPoints; //これに四隅の情報が入る。左上から時計回り
Rect r = new Rect(p[0].x,p[0].y,p[2].x,p[2].y);
painter.setStyle(Paint.Style.STROKE);
canvas.drawRect(r,painter); //枠の描画
painter.setStyle(Paint.Style.FILL);
canvas.drawText(barcode.rawValue,p[0].x,p[2].y,painter); //バーコードナンバーの描画
}
}
}
あとはonTouchEvent()にてバーコードの矩形内でタッチされたら処理へ移ればいい
コードを簡潔に説明すると
・カメラを立ち上げる
・カメラの描画更新時にBitmap化しBarcodeDetectorで解析
・Overlayに表示
となっており、実際にバーコードを取得しているのは
SparseArray<Barcode> barcodes = detector.detect(frame);
なのでカメラアプリをすでに作成していれば楽に追加できる
あとはカメラのフォーカスを工夫したりすれば、よりいいだろう
コメント
コメントを投稿