Androidでバーコードスキャナーを実装


はじめに

カメラやGPSなどハードウェア依存の機能を作る時、スマートフォンは便利ですよね。
アプリ版にしかない機能を盛り込む事で、Web版との差別化も出来ます。

今回は、Androidでバーコードスキャナー機能を実装してみたいと思います。

実行環境

インストール

diff --git a/app/build.gradle b/app/build.gradle
index be61190..22efbee 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,6 +6,11 @@ android {
     compileSdkVersion 29
     buildToolsVersion "29.0.3"

+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
     defaultConfig {
         applicationId "com.example.barcodescanner"
         minSdkVersion 24
@@ -29,6 +34,7 @@ dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     implementation 'androidx.core:core-ktx:1.3.1'
     implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
     testImplementation 'junit:junit:4.12'
     androidTestImplementation 'androidx.test.ext:junit:1.1.2'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

基本は、GitHubのREADMEに従ってbuild.gradleに記述し、
gradleを実行すればインストールされます。

メインアクティビティ作成

package com.example.barcodescanner

import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import com.google.zxing.integration.android.IntentIntegrator
import com.google.zxing.integration.android.IntentResult

class MainActivity : AppCompatActivity()
{
    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        val intentIntegrator = IntentIntegrator(this).initiateScan()
    }

    @SuppressLint("ShowToast")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
    {
        val result: IntentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)

        if (null != result) {
            val barcodeValue = result.contents

            if (null == barcodeValue) {
                Toast.makeText(this, "Read Error", Toast.LENGTH_LONG).show()
            } else {
                Toast.makeText(this, barcodeValue, Toast.LENGTH_LONG).show()
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data)
        }
    }
}

IntentIntegratorで、処理をバーコードスキャナーに移譲しています。
onActivityResult実装時のお作法として、
呼び出し元が想定されている機能である事を保証する為に、resultCodeの値を突き合わせたりしますが、
Zxingではその辺の処理をparseActivityResult()で行っています。

マニフェストに登録

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 227c879..1e9db0a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.barcodescanner">

@@ -7,6 +8,14 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/AppTheme" />
+        android:theme="@style/AppTheme"
+        android:hardwareAccelerated="true">
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>

 </manifest>

メインアクティビティをマニフェストに登録します。
その際、applicationタグの属性にandroid:hardwareAccelerated=trueを追加して下さい。

実行

アプリケーションを起動すると、バーコードスキャナーが表示されます。

バーコードをスキャンすると、値がポップアップで表示されます。

その他の設定

ビープ音をOFFにする

diff --git a/app/src/main/java/com/example/barcodescanner/MainActivity.kt b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
index 2040542..0fdb9ba 100644
--- a/app/src/main/java/com/example/barcodescanner/MainActivity.kt
+++ b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
@@ -14,7 +14,9 @@ class MainActivity : AppCompatActivity()
     {
         super.onCreate(savedInstanceState)

-        val intentIntegrator = IntentIntegrator(this).initiateScan()
+        val intentIntegrator = IntentIntegrator(this)
+            .setBeepEnabled(false)
+            .initiateScan()
     }

     @SuppressLint("ShowToast")

IntentIntegratorのインスタンス生成時、メソッドチェーンでsetBeepEnabled()を呼び出します。
その際、引数にfalseを渡すとビープ音がOFFになります。

メッセージを設定する

diff --git a/app/src/main/java/com/example/barcodescanner/MainActivity.kt b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
index 2040542..a591c19 100644
--- a/app/src/main/java/com/example/barcodescanner/MainActivity.kt
+++ b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
@@ -14,7 +14,9 @@ class MainActivity : AppCompatActivity()
     {
         super.onCreate(savedInstanceState)

-        val intentIntegrator = IntentIntegrator(this).initiateScan()
+        val intentIntegrator = IntentIntegrator(this)
+            .setPrompt("Test Message")
+            .initiateScan()
     }

     @SuppressLint("ShowToast")

IntentIntegratorのインスタンス生成時、メソッドチェーンでsetPrompt()を呼び出します。
その際、引数に文字列を渡すと画面のメッセージが上書きされます。

画面を横回転させない

package com.example.barcodescanner

import com.journeyapps.barcodescanner.CaptureActivity

class Zxing : CaptureActivity() {}
diff --git a/app/src/main/java/com/example/barcodescanner/MainActivity.kt b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
index 2040542..8155461 100644
--- a/app/src/main/java/com/example/barcodescanner/MainActivity.kt
+++ b/app/src/main/java/com/example/barcodescanner/MainActivity.kt
@@ -14,7 +14,9 @@ class MainActivity : AppCompatActivity()
     {
         super.onCreate(savedInstanceState)

-        val intentIntegrator = IntentIntegrator(this).initiateScan()
+        val intentIntegrator = IntentIntegrator(this).apply {
+            captureActivity = Zxing::class.java
+        }.initiateScan()
     }

     @SuppressLint("ShowToast")

CaptureActivityを継承したアクティビティを作成し、IntentIntegratorの呼び出し時に指定します。

後書き

OCRを勉強中です。
ハードウェア性能も関係しますが、文字の読み取り精度はどの程度なのでしょうか。
とても楽しみです。