Barcodes scannen unter Android: Ein Schritt-für-Schritt Tutorial

Melden

Dieses Tutorial zeigt dir, wie du eine moderne Android-App erstellst, die Barcodes und QR-Codes scannen kann. Wir verwenden dafür Google ML Kit (für die Erkennung) und CameraX (für den Kamera-Zugriff), da dies aktuell der performanteste und einfachste Weg ist.

1. Projekt vorbereiten (Dependencies)

Öffne deine build.gradle (Module: app) Datei und füge die notwendigen Bibliotheken für ML Kit und CameraX hinzu:

dependencies {
    // CameraX Core Bibliotheken
    def camerax_version = "1.3.0"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-view:${camerax_version}"

    // Google ML Kit Barcode Scanning
    implementation 'com.google.mlkit:barcode-scanning:17.2.0'
}

2. Berechtigungen in der AndroidManifest.xml

Damit die App auf die Kamera zugreifen darf, musst du die Erlaubnis im Manifest eintragen:

<uses-permission android:name="android.permission.CAMERA" />

<!-- Optional: Sorgt dafür, dass die App im Store nur für Geräte mit Kamera sichtbar ist -->
<uses-feature android:name="android.hardware.camera" android:required="true" />

3. Das Layout erstellen (activity_main.xml)

Wir benötigen eine PreviewView, um das Kamerabild anzuzeigen:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

</FrameLayout>

4. Den Barcode-Analyzer erstellen

Wir erstellen eine interne Klasse (oder eine separate Datei), die jedes Kamerabild analysiert.

import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage

class BarcodeAnalyzer(private val onBarcodeDetected: (String) -> Unit) : ImageAnalysis.Analyzer {

    private val scanner = BarcodeScanning.getClient()

    @androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

            scanner.process(image)
                .addOnSuccessListener { barcodes ->
                    for (barcode in barcodes) {
                        barcode.rawValue?.let { value ->
                            onBarcodeDetected(value)
                        }
                    }
                }
                .addOnCompleteListener {
                    // Wichtig: ImageProxy schließen, um das nächste Frame zu verarbeiten
                    imageProxy.close()
                }
        }
    }
}

5. Kamera in der Activity starten

In der MainActivity verknüpfen wir alles. Wir prüfen die Berechtigungen und starten den Kamera-Stream.

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.deineapp.databinding.ActivityMainBinding
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {

    private lateinit var cameraExecutor: ExecutorService
    private lateinit var previewView: androidx.camera.view.PreviewView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        previewView = findViewById(R.id.previewView)
        cameraExecutor = Executors.newSingleThreadExecutor()

        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 10)
        }
    }

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

        cameraProviderFuture.addListener({
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview Setup
            val preview = Preview.Builder().build().also {
                it.setSurfaceProvider(previewView.surfaceProvider)
            }

            // Analyzer Setup
            val imageAnalyzer = ImageAnalysis.Builder()
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor, BarcodeAnalyzer { barcodeValue ->
                        // Hier wird das Ergebnis verarbeitet
                        runOnUiThread {
                            Toast.makeText(this, "Scan: $barcodeValue", Toast.LENGTH_SHORT).show()
                        }
                    })
                }

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                cameraProvider.unbindAll()
                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer)
            } catch (exc: Exception) {
                // Fehler beim Starten der Kamera
            }

        }, ContextCompat.getMainExecutor(this))
    }

    private fun allPermissionsGranted() = ContextCompat.checkSelfPermission(
        baseContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED

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

So funktioniert es:

  1. CameraX liefert einen kontinuierlichen Stream von Bildern der Rückkamera.
  2. Die Methode setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) sorgt dafür, dass die App flüssig läuft, indem sie nur das aktuellste Bild analysiert und Frames überspringt, wenn der Prozessor zu langsam ist.
  3. ML Kit erkennt im Hintergrund den Barcode-Typ (QR, EAN, UPC etc.) und liefert den Textwert zurück.
  4. Wichtig: imageProxy.close() muss nach jeder Analyse aufgerufen werden, da die Kamera sonst keine neuen Bilder mehr liefert.

Tipps für die Praxis:

  • Vibration: Nutze den Vibrator-Service, um dem Nutzer haptisches Feedback bei einem erfolgreichen Scan zu geben.
  • Einmaliges Scannen: Implementiere eine Variable (z.B. isScanning = true), damit nicht 30-mal pro Sekunde derselbe Barcode erkannt und ein Toast angezeigt wird.
  • Fokus: CameraX kümmert sich in der Standardeinstellung automatisch um den Autofokus.
0