Androidアプリの勉強のため、QRコードリーダーの開発を行っています。
とりあえずQRコードを読み取る・生成する部分については実装することができました。
せっかくなのでご紹介しようと思います。
記事内に登場するコードは、下記リポジトリにて記載されているものが殆どです。
動作しているものを確認したい場合はご覧ください。
github.com
また、本記事は下記のQiita記事を参考にKotlinで実装を行ったもの+αとなります。
QRコードの読取、生成をする [Android] - Qiita
もくじ
1. ライブラリ導入
github.com
app/build.gradle
に下記の記述を追加して、ライブラリの導入を行います。
元記事ではminSdkVersionを15にしていましたが、最新のライブラリを利用するために24にしました。
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 24
targetSdkVersion 29
}
}
執筆時点で最新のリリースバージョンは4.0.2なので、そちらを利用しています。
dependencies {
...
implementation 'com.journeyapps:zxing-android-embedded:4.0.2'
}
2. QRコード読み込み
QRコード読み取り画面を起動
下記の処理を呼び出すことで、 QRコード読み取り画面を起動できます。
IntentIntegrator(this).initiateScan()
IntentIntegratorは、定義されているメソッドを通して各種設定値を変更することができます。
READMEに記載されているカスタマイズ例をKotlinで書くと下記のようになると思います。
IntentIntegrator(this).apply {
desiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES)
prompt("Scan a barcode")
cameraId(0)
beepEnabled(false)
barcodeImageEnabled(true)
}.initiateScan()
その他のオプションや詳細については、下記のソースを確認してください。
zxing-android-embedded/IntentIntegrator.java at master · journeyapps/zxing-android-embedded · GitHub
読み取った結果の取得・表示
IntentIntegratorで起動したActivityから結果を受け取ります。
result.contentsで読み取った結果を文字列で取り出すことができます。
下記のコードでは読み取った文字列をトーストで表示しています。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
when {
result != null -> Toast.makeText(this, result.contents, Toast.LENGTH_LONG).show()
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
3.文字列からQRコード生成
事前準備
変換対象の文字列入力用EditTextと、結果表示用ImageViewを配置します。
(コードなし)
文字列をQRコードに変換して画面上にセットする
BarcodeEncoder().encodeBitmap()
を用いて、文字列をQRコードに変換します。
sizeの500は適当な数値なので、用途に応じて変更してしまって大丈夫です。
EncodeHintType
のMapを渡さずにマルチバイト文字をQRコードにすると、decode時に化けてしまうので注意。
private fun makeQRCode(contents: String) {
val size = 500
try {
val bitmap = BarcodeEncoder().encodeBitmap(
contents,
BarcodeFormat.QR_CODE,
size,
size,
mapOf(EncodeHintType.CHARACTER_SET to "UTF-8")
)
imageView.setImageBitmap(bitmap)
} catch (e: WriterException) {
throw AndroidRuntimeException("Barcode Error.", e)
}
}
4.その他実装してみた機能
URLとして解釈できたらブラウザで起動
android.webkit.URLUtil.isValidUrl()
で、文字列がURLとして解釈可能か判定できます。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val result = IntentIntegrator
.parseActivityResult(requestCode, resultCode, data)
?.contents
when {
result.isNullOrEmpty() -> super.onActivityResult(requestCode, resultCode, data)
URLUtil.isValidUrl(result) -> dialogAction(
result,
"読み取りURLをブラウザで開きますか?",
"開く",
::openBrowser
)
else -> dialogAction(
result,
"読み取りテキストをクリップボードにコピーしますか?",
"コピー",
::copyToClipBoard
)
}
}
URLとして解釈できる場合に、URL型を渡して Intent.ACTION_VIEW
を起動してやることで、
デフォルトに設定されているブラウザが立ち上がり、読み取ったURLを表示することができました。
private fun openBrowser(contents: String) =
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(contents)))
テキストをクリップボードにコピー
android.content.ClipboardManager
を用いることで実現できます。
ClipData.newPlainText("", contents)
の第一引数にはラベルを設定できるようですが、
ユーザーからはあまり参照されない部分なので、とりあえず空文字で埋めてます。
private fun copyToClipBoard(contents: String) {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("", contents)
clipboard.setPrimaryClip(clip)
Toast.makeText(this, "クリップボードにコピーしました", Toast.LENGTH_SHORT).show()
}
他のアプリからテキスト共有でインテント起動
他のアプリからのIntent起動を許容するため、下記の記述を追加します。
* app/src/main/AndroidManifest.xml
<intent-filter>
<action androidname="android.intent.action.SEND" />
<category androidname="android.intent.category.DEFAULT" />
<data androidmimeType="text/*" />
</intent-filter>
その上で、渡ってきたintentに対して下記のように記述してやると値を取り出すことができます。
intent.dataString
で取れるよ、みたいなのを見たんですが、どうにもうまく取れなかったので、
実際は intent.getStringExtra(Intent.EXTRA_TEXT)
にて取り出しています。
val intentString = intent.dataString ?: intent.getStringExtra(Intent.EXTRA_TEXT)
取り出した文字列をEditTextにセットして、QRコード生成処理を呼び出すことで、
他のアプリから共有された文字列からQRコード生成を行うような実装ができました。
val intentString = intent.dataString ?: intent.getStringExtra(Intent.EXTRA_TEXT)
if (!intentString.isNullOrEmpty()) {
editText.setText(intentString)
makeQRCode(editText.text.toString())
}