ZxingでQRコードの読み描き – Android

少し前の記事で「Mobile Vision APIやる」って書いたばかりだけど、少し前の案件で使ったので、一応メモ。

リンク

Android用のZxingライブラリ
https://github.com/journeyapps/zxing-android-embedded

QRコード生成の参考はこちら
http://qiita.com/alingogo/items/3006e5685057c23db6bd

build.gradle

android {
    // 追加
    repositories {
        jcenter()
    }
}

dependencies {
    // appcompatの23以上が必要
    compile 'com.android.support:appcompat-v7:23.1.0'

    // 追加
    compile 'com.journeyapps:zxing-android-embedded:3.4.0'
}

あと、buildToolsVersionは 23.0.2 以上

Activity

QrActivity.java

public class QrActivity extends AppCompatActivity {
    private static final String TAG = QrActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qr);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
        if (result != null) {
            if (result.getContents() == null) {
                Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
            } else {
                // EditTextにQRコードの内容をセット
                EditText et = (EditText) findViewById(R.id.et);
                et.setText(result.getContents());
                et.setSelection(et.getText().length());
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_capture: // キャプチャ画面起動
                IntentIntegrator integrator = new IntentIntegrator(this);

                // Fragmentで呼び出す場合
                // IntentIntegrator integrator = IntentIntegrator.forFragment(this);

                // 独自でキャプチャ画面のActivityを作成
                // integrator.setCaptureActivity(ToolbarCaptureActivity.class);
                // → QrToolbarCaptureActivityのSample: https://github.com/journeyapps/zxing-android-embedded/blob/master/sample/src/main/java/example/zxing/ToolbarCaptureActivity.java

                // スキャンするバーコード形式を指定
                // integrator.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);

                // キャプチャ画面の下方にメッセージを表示
                integrator.setPrompt("Scan a barcode");

                // カメラの特定(この場合はフロントカメラを使用)
                // integrator.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT);

                // 読み取り時の音声をオフに
                integrator.setBeepEnabled(false);

                // バーコードを画像保存できるっぽい(保存先はonActivityResultでIntentResult#getBarcodeImagePath()で取得)
                integrator.setBarcodeImageEnabled(true);

                // スキャン画面の回転の制御
                integrator.setOrientationLocked(true);

                // キャプチャ画面起動
                integrator.initiateScan();


                break;
            case R.id.create_qr: // QRコード生成
                Bitmap bitmap;
                try {
                    EditText et = (EditText) findViewById(R.id.et);
                    String text = et.getText().toString();
                    if (text.equals("")) {
                        return;
                    }
                    bitmap = createQRCodeByZxing(et.getText().toString(), 480);
                    ImageView iv = (ImageView) findViewById(R.id.iv);
                    iv.setImageBitmap(bitmap);
                } catch (WriterException e) {
                    Log.e(TAG, "WriterException", e);
                    return;
                }
                break;
        }
    }

    // ここそのまま→ http://qiita.com/alingogo/items/3006e5685057c23db6bd
    // ありがとうございます。
    public Bitmap createQRCodeByZxing(String contents, int size) throws WriterException {
        //QRコードをエンコードするクラス
        QRCodeWriter writer = new QRCodeWriter();

        //異なる型の値を入れるためgenericは使えない
        Hashtable encodeHint = new Hashtable();

        //日本語を扱うためにシフトJISを指定
        encodeHint.put(EncodeHintType.CHARACTER_SET, "shiftjis");

        //エラー修復レベルを指定
        //L 7%が復元可能
        //M 15%が復元可能
        //Q 25%が復元可能
        //H 30%が復元可能
        encodeHint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);

        BitMatrix qrCodeData = writer.encode(contents, BarcodeFormat.QR_CODE, size, size, encodeHint);

        //QRコードのbitmap画像を作成
        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        bitmap.eraseColor(Color.argb(255, 255, 255, 255)); //いらないかも
        for (int x = 0; x < qrCodeData.getWidth(); x++) {
            for (int y = 0; y < qrCodeData.getHeight(); y++) {
                if (qrCodeData.get(x, y) == true) {
                    //0はBlack
                    bitmap.setPixel(x, y, Color.argb(255, 0, 0, 0));
                } else {
                    //-1はWhite
                    bitmap.setPixel(x, y, Color.argb(255, 255, 255, 255));
                }
            }
        }

        return bitmap;
    }
}

res/layout/activity_qr.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context=".activity.QrActivity">

    <Button
        android:id="@+id/start_capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/mg_m"
        android:onClick="onClick"
        android:text="@string/qr_start_capture"/>

    <EditText
        android:id="@+id/et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/mg_m"
        android:hint="@string/qr_result"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/border"/>

    <Button
        android:id="@+id/create_qr"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/mg_m"
        android:onClick="onClick"
        android:text="@string/qr_create"/>

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/mg_m"
        android:contentDescription="QRコード"/>

</LinearLayout>

AndroidManifest.xml

ライブラリ内のキャプチャ画面をセット

<activity
            android:name="com.journeyapps.barcodescanner.CaptureActivity"
            android:screenOrientation="fullSensor"
            tools:replace="screenOrientation"/>

独自のActivityも入れることができます。その辺はGithubのサンプルを参考に。

あと、CAMERAとFLASHLIGHTのパーミッションが必要なはずなんだけど、こっちで書かなくても使える……
ライブラリ側で吸収してくれてるのかな。不思議な感じ。

最初に「一応メモ」とは書いたものの、読み込みの精度は充分だと思うし、すごく簡単に実装できてすごい。

現場からは以上です!

screenshot_20161104-195807 screenshot_20161104-195959