📸

Jetpack ComposeでQRコードを読み取る

2024/02/15に公開

読んでほしい人

  • Jetpack Composeで開発をやっている人
  • QRコードのスキャンをやってみたい人

補足情報

こちらの動画を参考に進めてください。
https://www.youtube.com/watch?v=UKU9WizT7Bw&t=301s

そもそもQRコードとは?
https://nordvpn.com/ja/blog/qr-code-mechanism/

QRコード(キューアールコード)は、あらゆるデータ(数字、英字、漢字、カタカナ、ひらがな、記号、バイナリ、制御コードなど)を扱うことができる2次元コードのことで、1つのQRコードで最大文字数が7089文字(数字のみの場合)という大量のデータを格納できます。最近では、郵便局での送り状の作成や、SNSアカウントのログイン、ライブチケットのスキャンなど、いたるところで活用されています。

Android Studioの設定に慣れている必要があります。xmlをコピペするだけですが、設定する場面があります。こちらのGithubのxmlのコードが必要です。動画の通りにやっていけば、設定はできます。

https://github.com/highsource/aufzugswaechter-android-app/blob/master/app/src/main/res/drawable/qrcode_scan.xml

こちらを追加する。

<!-- drawable/qrcode-scan.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp"
    android:width="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path android:fillColor="#000" android:pathData="M4,4H10V10H4V4M20,4V10H14V4H20M14,15H16V13H14V11H16V13H18V11H20V13H18V15H20V18H18V20H16V18H13V20H11V16H14V15M16,15V18H18V15H16M4,20V14H10V20H4M6,6V8H8V6H6M16,6V8H18V6H16M6,16V18H8V16H6M4,11H6V13H4V11M9,11H13V15H11V13H9V11M11,6H13V10H11V6M2,2V6H0V2A2,2 0 0,1 2,0H6V2H2M22,0A2,2 0 0,1 24,2V6H22V2H18V0H22M2,18V22H6V24H2A2,2 0 0,1 0,22V18H2M22,22V18H24V22A2,2 0 0,1 22,24H18V22H22Z" />
</vector>

記事の内容

buidl.gradleにQRコードのライブラリを追加する:

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.qrapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.qrapp"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation(platform("androidx.compose:compose-bom:2023.08.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
    // QRのためのライブラリzxingを追加
    implementation ("com.journeyapps:zxing-android-embedded:4.3.0")
    implementation ("com.google.zxing:core:3.4.1")
}

QRコードのスキャンをする設定をAndroidManifest.xmlに追加する必要があります。

この設定を追加する:

<!-- QR CODEの設定を追加 -->
        <activity android:name="com.journeyapps.barcodescanner.CaptureActivity"
            android:screenOrientation="fullSensor"
            tools:replace="android:screenOrientation"
            />

さっきファイル作ったので、qr_scanが作れないので名前をqr_scan_testにしてます💦
drawableというディレクトを右クリックして、スクリーンショットのように、ファイルの新規作成をおこないます。

ファイル名を入力する

こちらの作成されたxmlファイルにGithubのxmlのコードを貼り付ける

修正後QRのiconが表示された!

デモ用のサンプルコード:

package com.example.qrapp

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import com.example.qrapp.ui.theme.QRAppTheme
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions

class MainActivity : ComponentActivity() {
    // QRコードの読み取り結果を格納する変数
    private var textResult = mutableStateOf("")
    // QRコードの読み取り結果を格納する変数を返す
    private  val barCodeLauncher = registerForActivityResult(ScanContract()) { result ->
        if (result.contents == null) {
            Toast.makeText(this@MainActivity, "Cancelled", Toast.LENGTH_LONG).show()
        } else {
            textResult.value = result.contents
        }
    }
    // カメラのパーミッションをリクエストする
    private  fun showCamera() {
        val options = ScanOptions()
        options.setDesiredBarcodeFormats(ScanOptions.QR_CODE)
        options.setPrompt("QRコードを読み取ってください")
        options.setCameraId(0)
        options.setBeepEnabled(false)

        barCodeLauncher.launch(options)
    }
    // カメラのパーミッションをリクエストする
    private  val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) {
        isGranted ->
        if(isGranted) {
            showCamera()
        }
    }

    @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            QRAppTheme {
                // QRコードの読み取り結果を表示するUI
                Scaffold(
                    bottomBar = {
                        BottomAppBar(
                            actions = {},
                            // 画面右下にQRコードを読み取るためのボタンを表示
                            floatingActionButton = {
                                FloatingActionButton(onClick = { checkCameraPermission(this@MainActivity) }) {
                                    Icon(
                                        painter = painterResource(id = R.drawable.qr_scan),
                                        contentDescription = "QR Scan"
                                    )
                                }
                            },
                        )
                    }
                ) { innerPadding ->
                    // QRコードの読み取り結果を画面中央に表示
                    Column(
                        modifier = Modifier.fillMaxSize()
                            .padding(innerPadding),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Image(
                            painter = painterResource(id = R.drawable.qr_scan),
                            modifier = Modifier.size(100.dp),
                            contentDescription = "QR"
                        )
                        // QRコードの読み取り結果をUIに表示
                        Text(text = textResult.value,
                            fontSize = 30.sp,
                            fontWeight = FontWeight.Bold
                        )
                    }
                }
            }
        }
    }
    // カメラのパーミッションをチェックする
    private fun checkCameraPermission(context: Context) {
      if(ContextCompat.checkSelfPermission(
          context,
          android.Manifest.permission.CAMERA
      ) == PackageManager.PERMISSION_GRANTED) {
            showCamera()
        } else if(shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)) {
          Toast.makeText(this@MainActivity, "Camera required", Toast.LENGTH_LONG).show()
      } else {
            requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)

      }
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    QRAppTheme {

    }
}

実際に使ってみる:

最後に

QRコードに興味がありネイティブで機能を実装してみました。Flutterではやったことなくて、ネイティブで今回実装してみましたが、まさかxml書くとは思いませんでしたね💦
ご興味ある方は試してみてください。

Jboy王国メディア

Discussion