[Android] ZXingでバーコードを生成・読み取りする ― CODE_128とNW-7(Codabar)の違いと実装

Android でバーコードというと QR コードの話が中心になりがちですが、実務では 1 次元バーコード(CODE_128 や NW-7 など)を扱う場面も少なくありません。会員証やクーポン、在庫管理のコードなど、意外と身近なところで登場します。

そして 1 次元バーコードでよくつまずくのが、「CODE_128 と NW-7(Codabar)のどちらを使うのか」という判断と、start / stop 文字の扱いです。この違いを知らずに実装すると、「生成はできたのにリーダーで読めない」といった地味なトラブルにハマります。この記事では、ZXing を使った生成・読み取りの基本と、この2つのフォーマットの違いを整理していきます。

スポンサーリンク

まず ZXing 系ライブラリの構成を押さえる

Android で ZXing を使うときは、役割の違う2つのライブラリを意識すると分かりやすいです。

  • com.google.zxing:core ― バーコードの生成・デコードを担う純 Java ライブラリ。画像(Bitmap)を作るのはこちら。
  • com.journeyapps:zxing-android-embedded ― カメラでスキャンするための Android 向けラッパー。読み取り UI を提供する。

依存関係はこう書きます(バージョンは最新の安定版に置き換えてください)。スキャンも生成も両方使う場合、embedded 側が core を内包しているので、これだけで足ります。

dependencies {
    // スキャン用(core を内包)
    implementation("com.journeyapps:zxing-android-embedded:X.Y.Z")
    // 生成だけなら core 単体でも可
    // implementation("com.google.zxing:core:X.Y.Z")
}

バーコードを生成する

生成は MultiFormatWriterBitMatrix を作り、BarcodeEncoderBitmap に変換して ImageView に表示する、という流れです。まずは CODE_128 の例から。

private fun generateCode128(content: String): Bitmap? {
    return try {
        val writer = MultiFormatWriter()
        // 幅600 × 高さ200 で CODE_128 を生成
        val bitMatrix = writer.encode(
            content, BarcodeFormat.CODE_128, 600, 200
        )
        BarcodeEncoder().createBitmap(bitMatrix)
    } catch (e: WriterException) {
        // 不正なコンテンツなどでエンコードに失敗
        null
    }
}

生成した Bitmap は、そのまま ImageView にセットするだけです。

val bitmap = generateCode128("SAMPLE12345")
if (bitmap != null) {
    binding.barcodeImage.setImageBitmap(bitmap)
}

CODE_128 と NW-7(Codabar)の違い

ここが本題です。まず前提として、NW-7 と Codabar は同じものを指します(日本では NW-7、海外では Codabar と呼ばれることが多い)。ZXing 上のフォーマット名は BarcodeFormat.CODABAR です。

2つの主な違いを整理すると、次のようになります。

  • 扱える文字:CODE_128 は数字・英大小文字・記号など ASCII 全般を扱える。NW-7 は数字と一部の記号(- $ : / . +)に限られる。
  • 密度:CODE_128 は高密度で、同じ桁数でも幅を抑えやすい。NW-7 は比較的低密度。
  • start / stop 文字:ここが最大の違い。NW-7 は start / stop 文字(A〜D)が仕様の一部で、コードの先頭と末尾に付く。CODE_128 は start / stop を内部的に持つため、コンテンツ側で意識する必要がない。
  • チェックデジット:CODE_128 は仕様上チェックサムを内蔵する。NW-7 は必須ではなく、運用側でルールを決めることが多い。
  • 用途:CODE_128 は物流・出荷ラベルなど汎用的に。NW-7 は図書館・血液バッグ・一部の会員証など、歴史的に使われてきた分野で今も現役。

実装で効いてくるのは start / stop

実装時にいちばん事故りやすいのが、この start / stop 文字です。NW-7 を生成するときは、コンテンツの先頭と末尾に start / stop 文字(A〜D)を含める前提で考えるのが安全です。

private fun generateCodabar(rawNumber: String): Bitmap? {
    // 例: start/stop に A を付ける(A + 本体 + A)
    val content = "A" + rawNumber + "A"
    return try {
        val bitMatrix = MultiFormatWriter().encode(
            content, BarcodeFormat.CODABAR, 600, 200
        )
        BarcodeEncoder().createBitmap(bitMatrix)
    } catch (e: WriterException) {
        null
    }
}

ZXing のエンコーダは start / stop が無いときにデフォルトを補うこともありますが、読み取り側(リーダーや連携先システム)が特定の start / stop(たとえば A…AA…B)を期待するケースがあります。連携仕様がある場合は、その指定に合わせて明示的に付けるのが確実です。「生成はできるのに連携先で弾かれる」ときは、まずこの start / stop を疑うとよいでしょう。

バーコードを読み取る(スキャン)

スキャンは ScanContractregisterForActivityResult を組み合わせるのが今の推奨です(従来の startActivityForResultIntentIntegrator は非推奨)。

// ランチャーは初期化時に登録する
private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
    if (result.contents == null) {
        // キャンセルされた
    } else {
        // 読み取った文字列
        onBarcodeScanned(result.contents)
    }
}
 
private fun startScan() {
    val options = ScanOptions().apply {
        // 読み取り対象のフォーマットを絞る
        setDesiredBarcodeFormats(
            ScanOptions.CODE_128,
            ScanOptions.CODABAR
        )
        setPrompt("バーコードを枠内に合わせてください")
        setBeepEnabled(true)
        setOrientationLocked(false)
    }
    barcodeLauncher.launch(options)
}

読み取り対象のフォーマットを setDesiredBarcodeFormats で絞っておくのがポイントです。指定しないと全フォーマットを対象にするため、似た形状のコードを誤読しやすくなります。扱うコードが決まっているなら、必ず絞り込んでおくと精度が安定します。

カメラを使うので、AndroidManifest.xml にカメラのパーミッションを宣言しておきます。実行時の権限リクエストはスキャン用の Activity 側で処理されます。

<uses-permission android:name="android.permission.CAMERA" />

ハマりやすいポイント

  • NW-7 の start / stop 忘れ ― 連携先が期待する start / stop を含めていないと、読めても検証で弾かれる。
  • フォーマットを絞っていない ― 全対象のままだと誤読しやすい。扱うコードだけを指定する。
  • NW-7 に非対応文字を入れる ― 英字や多くの記号は使えない。数字前提で設計する。
  • 非推奨 API の利用startActivityForResult / IntentIntegrator ではなく ScanContract を使う。

まとめ

  • 生成は MultiFormatWriterBarcodeEncoder、スキャンは ScanContractregisterForActivityResult
  • CODE_128 は高密度で ASCII 全般対応、start / stop は内部処理。汎用ならこちら。
  • NW-7(Codabar)は数字中心で、start / stop 文字(A〜D)を明示するのが実装の勘所。
  • スキャン時は対象フォーマットを絞って誤読を防ぐ。

1 次元バーコードは「動いた/動かない」が start / stop やフォーマット指定といった細かい仕様で決まりがちで、そこを押さえておくと調査もぐっと楽になります。連携仕様書がある場合は、フォーマット名と start / stop の指定を最初に確認しておくのがおすすめです。バーコードまわりの細かな話はまだあるので、また機会があれば書いていければと思います。

コメント

タイトルとURLをコピーしました