AndroidでQRコードのバイナリデータを取得する
AndroidでもiOSでもQRコードリーダアプリがたくさんありますが、QRコードのテキストデータ(URLとか)を読み取るものがほとんどと思います。
でも、QRコード自体はバイナリデータを含むことができます。3DSのソフトが発行するQRコードとかね。
今回はそんなQRコードのバイナリデータを Androidで取得する方法を紹介します。
「いや、サンプルとかいいからバイナリデータを読めるアプリが欲しいんだ」という方は、今回紹介するコードを組み込んだアプリを私がリリースしてますのでどうぞ
Android/iOS両方あるよ。是非ともご利用ください。ぜひ!
気を取り直して、AndroidでQRコードのバイナリデータを取得する方法です。
例によってコードの全文はgithubにあげてますのでそちらも参考にしてください。
動作確認環境
- macOS High Sierra 10.13.1
- Android Studio 3.0
- ZXing Android Embedded 3.5.0
準備
QRコードの読み取りには、ZXingというライブラリを使います。
ZXing はGoogleが開発している、様々な1次元/2次元バーコードの読み書きができるオープンソースのライブラリです。
多くの言語にポーティングされており、ZXingを直接利用することも出来ますが、ここではZXingをAndroid用により扱いやすくしたZXing Android Embeddedというライブラリを使います。
ZXing Android Embedded のライブラリの Github にも導入方法ありますが、アプリケーションの build.gradle で
1 2 3 4 5 |
dependencies { .... implementation 'com.journeyapps:zxing-android-embedded:3.5.0' } |
を追加することで使えるようになります。(Android Studio 3.0 より前のバージョンの場合は implementation ではなく compile)
以前は ZXing のコアライブラリもアプリに取り込む必要があったのですが、ZXing Android Embedded 3.0 以降はライブラリ単体のみ取り込めば動作するようになったそうです。
コード
ZXing Android Embedded の Activity を呼び出して使う方法もありますが、レイアウトに自由度がある、DecoratedBarcodeView というビューを配置する方法を紹介します。
レイアウトファイルに DecoratedBarcodeView を追加
通常のビューと同様に、レイアウトファイルで DecoratedBarcodeView を配置します。
例えば下記のような感じで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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=".MainActivity"> <com.journeyapps.barcodescanner.DecoratedBarcodeView android:id="@+id/decoratedBarcodeView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout> |
このビューを配置した範囲にカメラ画像が写ります。特に設定とかしなくてもガイドとメッセージが出ます。
こんな感じで。(下部の START SCAN のボタンは自分でつけてます。)
コード追加
続いて、QRコード読み取りのコードを追加します。
パーミッションチェックなどを省いて、QRコード処理部分だけを抜き出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
private var qrReaderView: DecoratedBarcodeView? = null private var startButton: Button? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) qrReaderView = findViewById(R.id.decoratedBarcodeView) startCapture() } private fun startCapture() { startButton?.isEnabled = false qrReaderView?.decodeSingle(object : BarcodeCallback { override fun barcodeResult(result: BarcodeResult?) { stopCapture() if (result == null) { // no result Log.w(TAG, "No result") return } Log.i(TAG, "QRCode Result: ${result.text}") val bytes = result.resultMetadata[ResultMetadataType.BYTE_SEGMENTS] as? List<*> val data = bytes?.get(0) as? ByteArray ?: return // print result val resultString = StringBuffer() data.map { byte -> resultString.append(String.format("0x%02X,", byte)) } Log.i(TAG, resultString.toString()) } override fun possibleResultPoints(resultPoints: MutableList<ResultPoint>?) { } }) qrReaderView?.resume() } |
手順としては、
- DecoratedBarcodeView を取得
- DecoratedBarcodeView#decodeSingle でリスナ登録
- DecoratedBarcodeView#resume で読み取り開始
です。
リスナで読み取り結果が BarcodeResult というクラスで通知されるので、そこから中身を取り出していきます。
よく利用されるテキストデータは、text というプロパティで取得できますが、バイナリデータは resultMetadata という別のプロパティにmapの一部として格納されています。
バイナリデータは、ResultMetadataType.BYTE_SEGMENTSというキーの値として保存されています。(バイト配列の配列として保存されています)
読み取り結果については ZXing のコアライブラリの方の Result クラスに対応していると思われますので、ResultクラスのAPIリファレンスも合わせて読むと理解が深まるかと。
細かい部分は github をご覧ください。
ちなみに上記サンプルはKotlinで書いてるので、Javaで同様のことをする場合はgetText()、getResultMetadata()で取得できます。
テスト
テキストデータから生成されたQRコード
こちらの画像のQRコードを読み込んだ場合。
このサイトのURLを埋め込んだソースになります。
結果のログ
1 2 3 |
I/MainActivity: QRCode Result: http://ryuta46.com/qrefine I/MainActivity: 0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x72,0x79,0x75,0x74,0x61,0x34,0x36,0x2E,0x63,0x6F,0x6D,0x2F,0x71,0x72,0x65,0x66,0x69,0x6E,0x65, |
テキストでURLが入っていて、バイナリデータとしてはURLがそのままコードになっている感じですね。
バイナリデータから生成されたQRコード
こちらの以前3DSのゲームアイテム配布用に作られたバイナリのQRコードの場合。
結果のログ
1 2 3 |
I/MainActivity: QRCode Result: qb��h�"~�����2�j��I+�*4/�bw�3�03ڼ��i�~ELU���u/L?�\BPBe�_3�wiJ�z��7r��̝o�W�)�'�6�H�m����&*�-l���X��;�e��D�4}��0�:U��P�!Y; ���\Sziߙj����<�4r���6�v0ω��V��$��LX>N�N� ,�-&����X>N�N� ,�-&����X>N�N� ,�-&���� I/MainActivity: 0x0C,0x71,0x62,0xED,0xD2,0x68,0xF8,0x22,0x7E,0x0A,0xDA,0xCA,0xE9,0x1A, ...(以下略)... |
テキストの方は化けてしまっていますが、バイナリ列は問題なく取得できていました。当然ながら、何を意味するバイナリ列なのかはわかりません。
さいごに
QRコード回りはAndroidもiOSでも標準ライブラリである程度読み込みなどできるようになってきてますが、ことバイナリデータになるとなかなか取得する方法がないのが現状です。
今回はAndroidでしたが、ZXing を使うとiOSでも同様のことができます。
今回の記事で反響がありそうであれば、iOS版の方も書いてみたいと思います。
2018/02/10 追記
そこそこ反響があったので、iOS版を書きました。
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする
Android Studio 3.0.1
macOS Sierra 10.12.6
ZXing Android Embedded 3.5.0
kotlin 1.1.15
でQRリーダーの実装を行なっており参考にさせていただいています。
アプリケーションの build.gradle で
implementation ‘com.journeyapps:zxing-android-embedded:3.5.0’
を追記しRebuild Gradleを実施。
レイアウトに下記DecoratedBarcodeViewを配置したところ、カメラ画面が表示されず、黒い画面が表示されてしまいます。
カメラのパーミッションが足らないのかと思い、manifestファイルに
しましたが変わりませんでした。
他修正点はございますでしょうか?
お手すきの際、ご返事いただけると幸いです。
コードが消えてしまったので再送します。
DecoratedBarcodeViewレイアウト
android:id=”@+id/decoratedBarcodeView”
android:layout_width=”0dp”
android:layout_height=”0dp”
app:layout_constraintBottom_toTopOf=”@id/buttonRestart”
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintRight_toRightOf=”parent”
app:layout_constraintTop_toTopOf=”parent”
manifestファイル
uses-permission android:name=”android.permission.CAMERA”/
uses-permission android:name=”android.permission.FLASHLIGHT”
こんにちは!閲覧ありがとうございます!
コード見てみました。
真っ黒、ということなのでビュー自体は問題なく配置ができている状態なのかなと思います。
本記事に書いている decodeSingle や resume などのメソッド呼び出しは問題なく行っていますでしょうか?
もし呼び出しを行っているが真っ黒、ということでしたら、ログなどに何かでているかもしれません。
そちらもご確認いただければと思います。
ご回答ありがとうございます。
ご指摘の通り、decodeSingle や resume などのメソッド呼び出しを行なっておらず、追加したら表示されるようになりました!
Android Studio 3.0.1
ZXing Android Embedded 3.5.0
の情報が少ないので非常に助かりました。引き続き参考にさせて頂きます。
連投+記事と関係ない話をしてしまいすみません。
ちなみにこの得られたバイナリデータから、連結(分割)QRコードであるかどうかは判断できるのでしょうか?
(QRヘッダー情報取得して、必要なQRコードのデータが揃った時点で次の処理を進る実装を考えています。)
http://programresource.net/2013/05/04/2188.html
上記の記事も参考にしているのですが、getRawBytes()に相当するkotlinの関数はあるのでしょうか?
もしよろしければ教えて頂きたいです。よろしくお願い致します。
Java の getXxxx は、すべて Kotlin からは xxxx というプロパティとしてアクセスできます。
ですので、今回の場合ですと result.rawBytes で Java の result.getRawBytes() と同じ結果が返ってくるはずです。
連結かどうか、という点はちょっとわからないですが、もし参考に提示いただいたリンク先の方法と同じ方法が使えるのであれば、rawBytes を使って実現できるかなと思います。
>Java の getXxxx は、すべて Kotlin からは xxxx というプロパティとしてアクセスできます。
そうなんですね、勉強になりました。
ありがとうございます。試してみます。
こんにちわ。参考になりました。
QRではなく普通のバーコードの場合はどこを変えれば良いのでしょうか?
記事に書いた実装のままでも、1次元のバーコードを読むことはできます。
ただし、今のコードのままでは読み込み完了時に resultMetadata へのアクセスで落ちてしまうので、その部分以降は削除してください。
もしQRコードには反応せず、1次元のバーコードにのみ反応して欲しい場合は、下記のように resume 前に設定を入れれば良いようです。現状は intent 経由でしか設定できないみたいですね。
val intent = IntentIntegrator(this)
intent.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES)
qrReaderView?.initializeFromIntent(intent.createScanIntent())
qrReaderView?.resume()
ありがとうございます。↓を消したらできました。
[ResultMetadataType.BYTE_SEGMENTS]
初心者なのでご指導よろしくお願いします。
再度投稿失礼します。
ZxingのQR読み込みスピードについて、何か設定を変更されていたりしますか?
読み込みスピードに関しては特に設定を変えたことはないですね。
そうなんですね。ありがとうございます!
もし可能であればkotlin–>Java の記述にすることはできますでしょうか?
Androidでdecodeするとエラーになってしまうので。
あとこのバーコードと他のJavaのソースを結合する時に障害となり前に進めないのでどうかお願いできます
でしょうか?
すみません、問題点が不明なので詳細教えてください。
> Androidでdecodeするとエラーになってしまうので。
コンパイル時のエラーでしょうか?実行時のエラーでしょうか?
エラーメッセージなど、詳細を教えてください。
> あとこのバーコードと他のJavaのソースを結合する時に障害となり
JavaとKotlin でソースコードのファイルを分けるのではダメなのでしょうか?
Java のコードを Kotlin のコードに変換するのは Android Studio で Code -> Convert Java File to Kotlin File でできますが、逆は手作業になります。
そもそも今のコードで発生している問題が Java に変換することで解決するのかがよくわからないので、お手数ですが詳細をご教示ください。
お世話になります。
返信遅くなりましてすみません。
>そもそも今のコードで発生している問題が Java に変換することで解決するのかがよくわからないので
> >あとこのバーコードと他のJavaのソースを結合する時に障害となり
Javaに変換しなくても、何日間か調べてようやく、変換しなくても用途を満たすことが今日できました。
>Java File to Kotlin File でできますが、逆は手作業になります。
逆はやっぱ手作業になるんですね。
強引なメールな送ってしまいご迷惑をおかけしました。でもこの解答で解決に向けて頑張ることができました。
ありがとうございました。
無事に解決できたようで良かったです!