ทำ feature scan QR code บน Android app อย่างง่าย ด้วย ML Kit

Android May 9, 2025

ถ้าแอพนึงอยากมี feature scan QR Code เมื่อก่อนเราต้องหาจาก 3rd-party library อื่นใด

แต่ตอนนี้มี ML Kit ใน Vision API ให้เราเลือกใช้แบบฟรี ๆ แถมวิธีใช้ก็ง่ายกว่าที่คิดอีกด้วย แล้วทำยังไง? ไปดูกัน!

น้องมิกกิถือมือถือสองมือ แสดงหน้าจอสแกน QR Code ด้วย ML Kit และ Google code scanner

ทำความรู้จัก ML Kit กันก่อน

ML Kit เป็นชุด Mobile SDK จาก Google ที่ช่วยให้เรานำ Machine Learning มาใช้ในแอป Android และ iOS ได้ง่ายๆ โดยใช้ Vision API และ Natural Language API บนแอพของเราได้เลย (ซึ่งเมื่อก่อนเขาอยู่บน Firebase แหละ ถ้าใครคุ้น ๆ)

และทั้งหมดใน ML Kit API แอพ Android ของเราต้องมี Android API Level ตั้งแต่ 21 ขึ้นไป

ตัวที่เป็น Vision API มี feature ต่าง ๆ ที่พ้น beta แล้ว (เพราะมันมีเยอะมาก) ดังนี้

  • Text Recognition v2: ถอด text ออกจากรูป ใช้ทำ OCR
  • Face Detection: แยกใบหน้าคนได้
  • Barcode Scanning: ใช้สำหรับ Scan QR Code และ Barcode ต่าง ๆ ในที่นี้เราจะใช้อันนี้กัน
  • Image Labeling: บอกว่าในภาพนี้มีอะไรบ้าง
  • Object Detection and Tracking: แยกวัตถุได้
  • Digital Ink Recognition: แกะลายมือได้ว่าเขียนอะไรมา ใช้เทคโนโลยีเดียวกันกับ Gboard, Google Translate, และเกมส์ Quick, Draw!
  • Custom models with ML Kit: สำหรับคนที่อยากทำ model เองในแอพของเรา

และในส่วนของ Barcode Scanning นั้น เป็นแบบ on-device ไม่ต้องใช้การเชื่อมต่ออินเทอร์เน็ต รองรับ format มาตรฐานเป็น 2 ประเภท

  • Linear formats: Codabar, Code 39, Code 93, Code 128, EAN-8, EAN-13, ITF, UPC-A, UPC-E
  • 2D formats: Aztec, Data Matrix, PDF417, QR Code

และบน Android พิเศษ สามารถ implement ได้ 2 แบบด้วยกัน

Scan barcodes with ML Kit on Android

แบบมาตรฐานสามัญ แบ่งเป็น 2 องค์ประกอบใหญ่ ๆ คือ ตัว ML Kit ที่ทำหน้าที่อ่าน barcode กับตัวกล้อง ในที่นี้เราจะใช้ Camera X กัน

เซ็ทอัพเซ็ทใจกันก่อน

ก่อนอื่นมา setup อะไรกันก่อน เอาแบบง่าย ๆ คือ เพิ่ม dependency ที่ build.gradle ของ module ที่เราทำการเพิ่มไปนั่นเอง มันจะมีให้เลือก 2 อัน คือ

dependencies {
    // ...

    // Use this dependency to bundle the model with your app
    implementation 'com.google.mlkit:barcode-scanning:17.3.0'
    
    // Use this dependency to use the dynamically downloaded model in Google Play Services
  implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
}
  • สำหรับการรวมตัว model ไปกับแอพ: เราใช้อันนี้
  • ใช้ model จาก Google Play: อันนี้โหลดมาเป็น dynamic แล้วต้องเพิ่ม config ในการ download แบบอัตโนมัติที่ AndroidManifest.xml ถ้าเลื่อนลงไปดู Google code scanner จะต่างอยู่นิดหน่อย
<application ...>
  ...
  <meta-data
      android:name="com.google.mlkit.vision.DEPENDENCIES"
      android:value="barcode"/>
  <!-- To use multiple models: android:value="barcode,model2,model3" -->
</application>

เริ่มเขียนโค้ดกัน

ของเราทำแบบง่าย ๆ คือกดปุ่มนี้ แล้วไปหน้า scanner โดยเราสร้าง activity ใหม่ที่ชื่อว่า BarcodeScannerActivity และแน่นอน มี activity แล้วต้องมี layout ให้ชื่อว่า activity_barcode_scanner ก็แล้วกัน

ข้างใน activity_barcode_scanner ก็แสนจะเรียบง่าย มี parent สักตัว และข้างในเป็น PreviewView เพื่อแสดงภาพจากกล้องให้เราดู

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".BarcodeScannerActivity">

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

กลับมาที่ BarcodeScannerActivity มาจัดการตัวแปรต่าง ๆ ที่ใช้กัน

  • binding: เข้าถึง layout xml activity_barcode_scanner ที่สร้างมาเมื่อกี้มาใช้ต่อ
  • cameraExecutor: ใช้ในการประมวลผลของกล้องที่ background thread เราสร้าง execution ตัวนี้เพื่อเอามาวิเคราะห์หา QR Code
  • barcodeBoxView: custom view ตัวหนึ่ง ที่เอามาวาดสี่เหลี่ยมตรง QR Code ของเรา แล้วเอามาเพิ่มโดยโค้ด ให้มันเต็มจอ
private lateinit var binding: ActivityBarcodeScannerBinding
private lateinit var cameraExecutor: ExecutorService
private lateinit var barcodeBoxView: BarcodeBoxView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityBarcodeScannerBinding.inflate(layoutInflater)
    setContentView(binding.root)

    cameraExecutor = Executors.newSingleThreadExecutor()

    barcodeBoxView = BarcodeBoxView(this)
    addContentView(
        barcodeBoxView,
        ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
    )
}

แล้ว BarcodeBoxView มันคืออะไร และหน้าตาเป็นยังไง? มันคือ View ที่แสดงกรอบสี่เหลี่ยมเพื่อบอกว่าตัวแอพตรวจจับเจอ QR Code แล้วว่าอยู่ตรงนี้นะ

เราสร้าง RectF เพื่อวาดกรอบสี่เหลี่ยมนี้ โดยใช้ Paint บอกว่ากรอบนี้เป็นสีแดง มีความหนา 5px และขอบมน 10px

การใช้งาน เรียกใช้ setRect() เพื่ออัพเดตตำแหน่ง QR Code ที่เจอ ว่า ได้ว่าอยู่ตรงไหน แล้วให้วาดสี่เหลี่ยมนี้ขึ้นมา ถ้าไม่เจอให้ลบของเก่าออกโดยการใส่ RectF เปล่า ๆ ลงไป

class BarcodeBoxView(context: Context) : View(context) {

    private val paint = Paint()

    private var mRect = RectF()

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val cornerRadius = 10f

        paint.style = Paint.Style.STROKE
        paint.color = Color.RED
        paint.strokeWidth = 5f

        canvas?.drawRoundRect(mRect, cornerRadius, cornerRadius, paint)
    }

    fun setRect(rect: RectF) {
        mRect = rect
        invalidate()
        requestLayout()
    }
}

เพิ่ม permission ของกล้องกันก่อนเลย เมื่อ install app มาใหม่ จะเจอหน้าให้ allow camera permission กันก่อน แต่ถ้าเคย aollow permission แล้ว ก็ไปเปิดกล้อง ถ้าไม่จะมี popup dialog บอกว่าเราต้องการ permission ในการใช้กล้องเพื่อ process barcode น้า

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    checkCameraPermission()
}

/**
* This function is executed once the user has granted or denied the missing permission
*/
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    checkIfCameraPermissionIsGranted()
}

/**
 * This function is responsible to request the required CAMERA permission
*/
private fun checkCameraPermission() {
    try {
        val requiredPermissions = arrayOf(Manifest.permission.CAMERA)
        ActivityCompat.requestPermissions(this, requiredPermissions, 0)
    } catch (e: IllegalArgumentException) {
        checkIfCameraPermissionIsGranted()
    }
}

/**
* This function will check if the CAMERA permission has been granted.
* If so, it will call the function responsible to initialize the camera preview.
* Otherwise, it will raise an alert.
*/
private fun checkIfCameraPermissionIsGranted() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
        // Permission granted: start the preview
        startCamera()
    } else {
        // Permission denied
        MaterialAlertDialogBuilder(this)
            .setTitle("Permission required")
            .setMessage("This application needs to access the camera to process barcodes")
            .setPositiveButton("Ok") { _, _ ->
                // Keep asking for permission until granted
                checkCameraPermission()
            }
            .setCancelable(false)
            .create()
            .apply {
                 setCanceledOnTouchOutside(false)
                 show()
            }
        }
    }

เมื่อเรา allow camera กันเรียบร้อยแล้ว มาต่อกันที่ startCamera() ตรงนี้จะเปิด Camera X และเรียกใช้ ML Kit กันล่ะ

เริ่มจากสร้าง ProcessCameraProvider ที่มีชื่อว่า cameraProviderFuture จากนั้นมาเพิ่ม listener กัน โดยดึงตัว provider ที่ชื่อว่า cameraProvider ขึ้นมา

เอ้อออ ในที่นี้เราจะ execute บน main thread เนอะ เพราะเราใช้กล้อง

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
    }, ContextCompat.getMainExecutor(this))
}

สร้าง Preview ขึ้นมา ให้ตัว surface provider เป็นตัว PreviewView ที่เราสร้างไว้ใน layout xml ตอนแรกเลย

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        
        // Preview
        val preview = Preview.Builder()
            .build()
            .also {
               it.setSurfaceProvider(binding.previewView.surfaceProvider)
            }
    }, ContextCompat.getMainExecutor(this))
}

มาถึงส่วนสำคัญ ส่วน image analyzer สร้างตัว ImageAnalysis ขึ้นมา เนื่องจากเราใช้ Camera X เขาให้ใส่ ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST ที่ setBackpressureStrategy() เพื่อการันตีว่าจะมีเพียงรูปเดียวที่จะส่งไป analyze ในแต่ละครั้ง และ setAnalyzer() ด้วย cameraExecutor ที่สร้างไว้เมื่อกี้ กับตัว Analyzer ที่เราสร้างใหม่ ชื่อว่า QrCodeAnalyzer

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        
        // Preview
        val preview = Preview.Builder()
            .build()
            .also {
               it.setSurfaceProvider(binding.previewView.surfaceProvider)
            }
    }, ContextCompat.getMainExecutor(this))
}

แล้ว QrCodeAnalyzer ข้างในมีอะไรบ้าง? เป็นส่วนในการทำ image analysis ด้วย ML Kit นี่แหละ สืบทอดมาจาก ImageAnalysis.Analyzer

มี parameter ที่ต้องโยนเข้ามา 4 อันด้วยกัน คือ

  • context: ใช้ show toast ว่าได้ value ของ QR Code ออกมาเป็นอะไร ในการ implement จริงอาจจะไปเปิดหน้าเว็บ หรืออื่นใดแล้วแต่เลย ถ้าเปิดเว็บอาจจะใช้เป็น parameter ชนิดอื่นแทนเนอะ
  • barcodeBoxView: View กรอบสี่เหลี่ยม เอามาวาดในนี้
  • previewViewWidth: ความกว้างของ PreviewView
  • previewViewHeight: ความสูงของ PreviewView

ก่อนอื่น สร้างตัวแปร เพื่อใช้ในการ scale factor กับตัว PreviewView และภาพจากกล้อง ทั้งแกน x และ y

private var scaleX = 1f
private var scaleY = 1f

หลัก ๆ เรา override ตัว analyze() เพื่อทำการรับภาพมาวิเคราะห์ทีละภาพ

ก่อนอื่นดูว่ากล้องมีภาพไหม ถ้ามีภาพ ไปคำนวณ scale factors ต่อ และแปลงภาพที่ได้เอาไปใช้ต่อใน ML Kit

override fun analyze(image: ImageProxy) {
    val img = image.image
    if (img != null) {
        // Update scale factors
        scaleX = previewViewWidth / img.height.toFloat()
        scaleY = previewViewHeight / img.width.toFloat()
        
        val inputImage = InputImage.fromMediaImage(img, image.imageInfo.rotationDegrees)
    }
}

จุดสำคัญ เอา ML Kit ไปใช้ล่ะ โดยการ config barcode scanner โดยเรา detect QR Code อย่างเดียว ใส่เป็น Barcode.FORMAT_QR_CODE หรือเอา barcode ทั้งหมดก็ใส่ Barcode.FORMAT_ALL_FORMATS

ใส่ enableAllPotentialBarcodes() เป็น optional ในการ return barcode ทั้งหมดที่เป็นไปได้ แม้จะ decode ออกมาไม่ได้ก็ตาม

override fun analyze(image: ImageProxy) {
    // ...
    if (img != null) {
        // ...
        
        // Process image searching for barcodes
        val options = BarcodeScannerOptions.Builder()
            .setBarcodeFormats(
                Barcode.FORMAT_QR_CODE
            )
            .enableAllPotentialBarcodes() // Optional
            .build()
    }
}

สร้างตัวแปร scanner เพื่อเอา instance ของ BarcodeScanner แล้วใส่ option เมื่อกี้ลงไป ถ้าไม่มีก็ไม่ต้องใส่

override fun analyze(image: ImageProxy) {
    // ...
    if (img != null) {
        // ...
        
        // Get an instance of BarcodeScanner
        val scanner = BarcodeScanning.getClient(options)
        // or not option
        //val scanner = BarcodeScanning.getClient()
    }
}

จากนั้นประมวลผลภาพ ผ่าน listener 2 อัน

  • addOnSuccessListener: ได้ค่า barcode แล้วเอาไปทำอะไรต่อ ในที่นี้ success แล้ว barcode ไม่ empty จะ show toast ว่าได้ value อะไรออกมา และวาดสี่เหลี่ยมขึ้นมาตรง QR Code นั้นที่เจอ และถ้าได้ empty ก็ลบทิ้ง
  • addOnFailureListener: ถ้าเกิด failure จะทำยังไงต่อ
override fun analyze(image: ImageProxy) {
    // Process the image
    scanner.process(inputImage)
        .addOnSuccessListener { barcodes ->
            if (barcodes.isNotEmpty()) {
                for (barcode in barcodes) {
                    // Handle received barcodes...
                    Toast.makeText(
                        context,
                        "Value: " + barcode.rawValue,
                        Toast.LENGTH_SHORT
                    ).show()
                    // Update bounding rect
                    barcode.boundingBox?.let { rect ->
                        barcodeBoxView.setRect(
                            adjustBoundingRect(
                                rect
                            )
                        )
                    }
                }
            } else {
                // Remove bounding rect
                barcodeBoxView.setRect(RectF())
            }
        }
        .addOnFailureListener { }
}

เพิ่มเติมในส่วน barcode information มันจะคืนออกมาเป็น object ที่ชื่อว่า Barcode ข้างในมีอะไรที่เราเอาไปใช้ต่อได้บ้าง

  • barcode.rawValue: ค่าที่อ่านได้จาก barcode นั้น ๆ
  • barcode.valueType: type ของ barcode นั้น ๆ มีหลายอันเลย ที่คิดว่าใช้หลัก ๆ จะมี TYPE_URL, TYPE_TEXT, TYPE_WIFI โดยแต่ละ type จะมี object แตกต่างกันออกไป

เช่น TYPE_URL เป็น object UrlBookmark การใช้งานดึง barcode.url?.title และ barcode.url?.url ออกมาได้ เผื่อใช้ในการเปิดหน้าเว็บงี้

ส่วน TYPE_WIFI เป็น object WiFi สามารถดึง barcode.wifi?.ssid, barcode.wifi?.password และ barcode.wifi?.encryptionType

  • barcode.cornerPoints: มุมของตัว barcode นั้น
  • barcode.boundingBox: กรอบที่เจอ barcode นั้น ๆ

ในส่วนการคำนวณกรอบสี่เหลี่ยมที่แสดงล้อมรอบ QR Code มี adjustBoundingRect() โดยตัว rect เอามาจาก barcode.boundingBox แล้วมาวาดตามตำแหน่งที่เจอ

private fun translateX(x: Float) = x * scaleX
private fun translateY(y: Float) = y * scaleY

private fun adjustBoundingRect(rect: Rect) = RectF(
    translateX(rect.left.toFloat()),
    translateY(rect.top.toFloat()),
    translateX(rect.right.toFloat()),
    translateY(rect.bottom.toFloat())
)

ปิดจบโดยการปิดตัว image เพื่อให้ตัว Camera X ส่งภาพใหม่ในการประมวลผล

override fun analyze(image: ImageProxy) {
    // ...
    if (img != null) {
        // ...
    }
    image.close()
}

กลับมาที่ BarcodeScannerActivity เลือก default เป็นกล้องหลัง

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // ...
        
        // Select back camera as a default
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
            
    }, ContextCompat.getMainExecutor(this))
}

และเอา cameraProvider ไป unbind ของเดิมออก แล้ว bind ใหม่กับ lifecycle อย่าลืมครอบ try-catch และจัดการ exception ด้วยล่ะ

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // ...
        
        try {
            // Unbind use cases before rebinding
            cameraProvider.unbindAll()

            // Bind use cases to camera
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageAnalyzer
            )
         } catch (exc: Exception) {
            exc.printStackTrace()
         }
            
    }, ContextCompat.getMainExecutor(this))
}

เมื่อ user ใช้หน้านี้เสร็จแล้ว ปิดหน้านี้ทิ้ง เราจะปิด executor เพื่อป้องกัน memory leak เมื่อเราไม่ใช้งานหน้านี้แล้ว

override fun onDestroy() {
    super.onDestroy()
    cameraExecutor.shutdown()
}

Reference

Scan barcodes with ML Kit on Android | Google for Developers
https://developers.google.com/ml-kit/vision/barcode-scanning/android
ML Kit Analyzer | Android media | Android Developers
https://developer.android.com/media/camera/camerax/mlkitanalyzer
QR Scanning using CameraX
QR scanning is a common camera use case that developers want to implement in their apps, with CameraX & zxing it’s easy to implement.
https://medium.com/@msasikanth/qr-scanning-using-camerax-4757ed3687f8

จริง ๆ มีของอีกคนนึง แต่เขาลบบทความบน Medium ออกแล้ว แง

Google code scanner (Android only)

อันนี้ไม่ต้องใช้ permission camera ใช้งานได้ง่ายกว่าแบบปกติด้วย โดยใช้ ML Kit Barcode Scanning API เหมือนกัน และ return Barcode ออกมาเหมือนกัน

และอันนี้มีใช้เฉพาะ Android เท่านั้น แล้วมี auto-zoom ให้ด้วยนะ

การใช้งาน implement ได้ง่าย กดปุ่มแล้วจะพาไปหน้า activity ที่ scan code เมื่อ success แล้ว เอาค่าที่ได้ไปใช้งานต่อได้ทันที ง่ายเกินจนงง (แต่ความจริงไม่น่ามีแอพไหนใช้อันนี้มั้ง555)

ก่อนอื่นไปไปที่ settings.gradle เพื่อใส่ Maven repository ของ Google และ maven central ก่อนดังนี้

dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
  }
}

จากนั้นเพิ่มของใน dependency ที่ build.gradle อีกเช่นเคย

dependencies {
    // ...

    // Google code scanner
    implementation("com.google.android.gms:play-services-code-scanner:16.1.0")
}

แล้วก็ไป config ที่ AndroidManifest.xml ว่าแอพเราใช้ Google Play service นะ เพื่อให้มัน download scanner module เข้ามาลงเครื่องเราอัตโนมัติ เมื่อแอพเราติดตั้งจาก Play Store

<application ...>
  ...
  <meta-data
      android:name="com.google.mlkit.vision.DEPENDENCIES"
      android:value="barcode_ui"/>
</application>

ต่อมาเรามาเข้าโค้ดกัน ก่อนอื่นเลย set option ของตัว scanner ก่อน ว่าเราจะสแกนกับ QR Code นะ ช่วย auto zoom ให้หนูด้วย

val options = GmsBarcodeScannerOptions.Builder()
    .setBarcodeFormats(
        Barcode.FORMAT_QR_CODE
    )
    .enableAutoZoom()
    .build()

จากนั้นสร้าง instance ของ GmsBarcodeScanner

val scanner = GmsBarcodeScanning.getClient(this, options)

จบด้วยเริ่ม scan แล้วตามด้วย listener ทั้ง 3 อันว่า

  • addOnSuccessListener: ถ้า success จะทำอะไรต่อ โดยค่าจาก QR Code ที่เราได้ จะเป็น barcode.rawValue
  • addOnCanceledListener: ถ้า user cancel จะทำอะไร
  • addOnFailureListener: ถ้า failure จาก exception ใด ๆ จะ handle ยังไงต่อ อันนี้ใส่ตามใจเราเลย
scanner.startScan()
    .addOnSuccessListener { barcode ->
        // Task completed successfully
        val rawValue: String? = barcode.rawValue
        Log.d("addOnSuccessListener", rawValue.toString())
        Toast.makeText(this, rawValue, Toast.LENGTH_SHORT).show()
    }
    .addOnCanceledListener {
        // Task canceled
        Log.d("addOnCanceledListener", "")
    }
    .addOnFailureListener { e ->
        // Task failed with an exception
        Log.d("addOnFailureListener", e.message.toString())
    }

รวมร่าง เช่น เรากดปุ่ม เพื่อไปเปิด Google code scanner

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.buttonGoogleScanner.setOnClickListener {
            openGoogleCodeScanner()
        }
    }

private fun openGoogleCodeScanner() {
    val options = GmsBarcodeScannerOptions.Builder()
        .setBarcodeFormats(
            Barcode.FORMAT_QR_CODE
        )
        .enableAutoZoom()
        .build()
    val scanner = GmsBarcodeScanning.getClient(this, options)
    scanner.startScan()
        .addOnSuccessListener { barcode ->
            // Task completed successfully
            val rawValue: String? = barcode.rawValue
            Log.d("addOnSuccessListener", rawValue.toString())
            Toast.makeText(this, rawValue, Toast.LENGTH_SHORT).show()
        }
        .addOnCanceledListener {
            // Task canceled
            Log.d("addOnCanceledListener", "")
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            Log.d("addOnFailureListener", e.message.toString())
        }
    }

ข้อควรระวัง: Google code scanner ใช้ได้เฉพาะเครื่องจริงเท่านั้น ในเครื่อง Android Emulator เปิดไม่ได้จ้า

Reference

Google code scanner (Android only) | ML Kit | Google for Developers
https://developers.google.com/ml-kit/vision/barcode-scanning/code-scanner

one more thing

เนื่องจาก code เยอะมาก เลยรวมทั้งหมดใน Github จะได้ดูตามได้ง่ายขึ้นน้า

GitHub - mikkipastel/mlbarcode: sample app for using ML Kit to scan QR code
sample app for using ML Kit to scan QR code. Contribute to mikkipastel/mlbarcode development by creating an account on GitHub.
https://github.com/mikkipastel/mlbarcode

หวังว่าอ่านแล้วจะไม่งงกันนะ


ติดตามข่าวสารตามช่องทางต่าง ๆ และทุกช่องทางโดเนทกันไว้ที่นี่เลย แนะนำให้ใช้ tipme เน้อ ผ่าน promptpay ได้เต็มไม่หักจ้า

ติดตามข่าวสารแบบไว ๆ มาที่ Twitter เลย บางอย่างไม่มีในบล็อก และหน้าเพจนะ

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.