Androidでバーコードを読み込む

 ふと思い立ちAndroidにてバーコードを読み込めないかと調べてみる

ネットで検索すると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);
  }
}

クラスを作成し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();
    }
  }
}

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); //バーコードナンバーの描画
    }
  }
}

あとはonTouchEvent()にてバーコードの矩形内でタッチされたら処理へ移ればいい
 
コードを簡潔に説明すると
・カメラを立ち上げる
・カメラの描画更新時にBitmap化しBarcodeDetectorで解析
・Overlayに表示
 
となっており、実際にバーコードを取得しているのは
SparseArray<Barcode> barcodes = detector.detect(frame);
なのでカメラアプリをすでに作成していれば楽に追加できる
あとはカメラのフォーカスを工夫したりすれば、よりいいだろう
 

コメント

このブログの人気の投稿

Firefoxで改造アドオンのインストール失敗

base64で文字列化された画像のリサイズ用ツール

PlayOnLinuxでインストールリストが表示されない場合の対処法