Android实现二维码扫描功能(附带源码)

一、项目介绍

1.1 背景与意义

二维码(QR Code)作为一种二维条码,因其存储容量大、识别速度快、容错能力强,被广泛用于商品追溯、电子票务、移动支付、身份验证等场景。在 Android 应用中集成二维码扫描功能,能够帮助用户快速读取二维码中的信息,如 URL、文本、联系方式、Wi‑Fi 配置等,大大提升用户体验。

本项目旨在从零开始演示如何在 Android 应用中实现二维码扫描功能,并深入探讨其背后的原理、关键技术与最佳实践。文章将涵盖以下内容:

  1. 相关技术概览:二维码原理、常用开源库对比;

  2. 项目架构与依赖:如何选择相机 API 与扫描库;

  3. 核心实现思路:相机预览、图像格式转换、异步解码;

  4. 完整代码整合Activity、自定义 View、布局 XML、权限申请、回调接口全部集中展示;

  5. 代码详解:剖析每个方法的功能与关键点;

  6. 性能与兼容性优化:预览分辨率、线程调度、权限流程、Android 6.0+ 运行时申请;

  7. 拓展方向:连续扫描、识别条形码、嵌入 WebView、Compose 重写、深度学习优化。


二、相关技术概览

2.1 二维码原理

  • QR Code:由日本 Denso Wave 在 1994 年发明,支持中文版字符及多语言。

  • 版本与纠错:共有 40 个版本(尺寸从 21×21 到 177×177),纠错等级分为 L、M、Q、H,对应可恢复 7%、15%、25%、30% 的损毁区域。

  • 编码模式:支持数字、字母数字、字节(UTF‑8)、汉字。

2.2 常用开源扫描库

库名GitHub优点缺点
ZXingzxing/zxing成熟稳定、支持多种条码类型原生 Java 解码性能一般,UI 较陈旧
ZBarZBar/ZBarC 语言实现,速度快集成略繁琐,滤镜识别需自行配置
Google ML Kit-谷歌官方、后续维护、支持多种 ML 功能需联网或下载模型,体积较大
ZXing Android Embeddedjourneyapps/zxing-android-embedded封装好的 CameraX/Camera2 扩展,集成便利依赖较重,定制化需深入源码

本指南采用 ZXing Android Embedded(基于 ZXing 但封装了摄像头预览与解码线程),兼顾成熟度与集成便捷性,也会简要介绍原生 CameraX + ML Kit 的实现思路。


三、项目架构与依赖

3.1 Gradle 依赖

// 项目 build.gradle
buildscript {
    repositories { google(); mavenCentral() }
    dependencies { classpath 'com.android.tools.build:gradle:7.4.0' }
}

// 模块 build.gradle
plugins {
    id 'com.android.application'
}
android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.qrcodescanner"
        minSdk 21
        targetSdk 34
    }
    buildFeatures { viewBinding true }
}
dependencies {
    implementation 'com.journeyapps:zxing-android-embedded:4.3.0'  // ZXing 封装
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.camera:camera-camera2:1.4.0'         // 若使用 CameraX
    implementation 'androidx.camera:camera-lifecycle:1.4.0'
    implementation 'androidx.camera:camera-view:1.4.0'
    // 如使用 ML Kit:
    // implementation 'com.google.mlkit:barcode-scanning:17.0.2'
}

3.2 权限申请

AndroidManifest.xml 声明:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera.any" android:required="false"/>

在 Android 6.0+ 运行时动态申请 CAMERA 权限,用户拒绝时需友好提示或引导。


四、核心实现思路

4.1 使用 ZXing Android Embedded

  1. 布局中添加 DecoratedBarcodeView:它封装了预览、解码、激光线等 UI。

  2. 通过 barcodeView.decodeContinuous(callback):启动连续解码模式。

  3. BarcodeCallback 中获取解码结果:在回调内更新 UI 或启动下一个 Activity。

  4. 生命周期管理:在 onResume() 调用 barcodeView.resume();在 onPause() 调用 barcodeView.pause()

4.2 原生 CameraX + ML Kit

  1. CameraX Preview:通过 PreviewViewProcessCameraProvider 获取预览。

  2. ImageAnalysis:创建 ImageAnalysis 用以拿到相机帧,设置合适的分辨率与回调线程。

  3. ML Kit 扫码:在 analyzer 中使用 BarcodeScanning.getClient()InputImage.fromMediaImage() 进行检测。

  4. 结果回调:在主线程更新扫描结果。


五、完整代码整合

<!-- ==================== File: res/layout/activity_main.xml ==================== -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- ZXing 专用扫码视图 -->
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/barcode_scanner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_framing_rect_width="300dp"
        app:zxing_framing_rect_height="300dp"
        app:zxing_viewfinder_laser_color="@color/colorAccent"/>

    <!-- 结果覆盖层 -->
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#88000000"
        android:padding="16dp"
        android:textColor="#FFFFFF"
        android:textSize="16sp"/>
</FrameLayout>
// ==================== File: MainActivity.java ====================
package com.example.qrcodescanner;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import android.widget.TextView;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.journeyapps.barcodescanner.*;

public class MainActivity extends AppCompatActivity {

    private DecoratedBarcodeView barcodeView;
    private TextView tvResult;

    // 扫码回调
    private BarcodeCallback callback = new BarcodeCallback() {
        @Override
        public void barcodeResult(BarcodeResult result) {
            if (result.getText() != null) {
                // 停止预览避免持续回调
                barcodeView.pause();
                tvResult.setText("扫描结果: " + result.getText());
                Toast.makeText(MainActivity.this,
                        "扫码成功: " + result.getText(),
                        Toast.LENGTH_LONG).show();
            }
        }
        @Override public void possibleResultPoints(java.util.List<ResultPoint> list) {}
    };

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

         // 1. 绑定视图
        barcodeView = findViewById(R.id.barcode_scanner);
        tvResult    = findViewById(R.id.tv_result);

        // 2. 动态申请相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            initScanner();
        } else {
            requestPermissionLauncher.launch(Manifest.permission.CAMERA);
        }
    }

    // 处理权限回调
    private final androidx.activity.result.ActivityResultLauncher<String>
        requestPermissionLauncher = registerForActivityResult(
        new ActivityResultContracts.RequestPermission(),
        isGranted -> {
            if (isGranted) initScanner();
            else Toast.makeText(this,
                    "需要相机权限才能扫描二维码",
                    Toast.LENGTH_LONG).show();
        });

    // 初始化并启动扫码
    private void initScanner() {
        barcodeView.decodeContinuous(callback);
    }

    @Override
    protected void onResume() {
        super.onResume();
        barcodeView.resume(); // 恢复相机预览与解码
    }

    @Override
    protected void onPause() {
        super.onPause();
        barcodeView.pause();  // 暂停预览与解码
    }
}

说明: ZXing Android Embedded 默认会在扫码区域显示绿色激光线,并在设备支持时自动切换闪光灯。你也可以通过 barcodeView.setTorchListener(...) 和 XML 属性 zxing_use_texture_view 做进一步定制。


六、核心代码解读

  1. DecoratedBarcodeView

    • 封装了扫码预览(CameraPreview)、扫码框(ViewfinderView)、激光线、提示文本等 UI;

    • decodeContinuous(BarcodeCallback) 启动连续解码,回调在任意线程;

  2. BarcodeCallback.barcodeResult(...)

    • 当 ZXing 成功识别到一帧二维码时回调,返回 BarcodeResult

    • 推荐在首次识别后调用 barcodeView.pause(),避免重复识别;

  3. 权限管理

    • 使用 ActivityResultContracts.RequestPermission API 动态请求 CAMERA 权限;

    • 若被拒绝,应提示用户并退出或禁用扫码功能;

  4. 生命周期管理

    • onResume() 调用 barcodeView.resume(),恢复相机与解码线程;

    • onPause() 调用 barcodeView.pause(),释放相机资源;


七、性能与兼容性优化

  1. 预览分辨率

    • 默认使用相机最高可用分辨率,可能影响解码速度;

    • 可通过 barcodeView.getBarcodeView().getCameraSettings().setRequestedCameraResolution(...) 设置合适尺寸,如 1280×720;

  2. 线程调度

    • ZXing Embedded 已在后台执行解码,无需手动管理;

    • 若改用 CameraX + ML Kit,需在 ImageAnalysissetAnalyzer(Executors.newSingleThreadExecutor(), analyzer) 中指定单线程解码,避免 UI 线程阻塞。

  3. 闪光灯控制

    • 若光线不足,可在 UI 中显示“打开/关闭闪光灯”按钮;

    • 使用 barcodeView.setTorchOn() / setTorchOff()

  4. 平滑 UX

    • 在扫码框周围显示半透明遮罩,突出扫码区域;

    • 使用动画提示用户对准二维码;


八、扩展方向

  1. 条码/二维码混合识别

    • ZXing 支持多种格式:EAN‑13、Code 39、PDF417、Data Matrix 等;

    • 通过 barcodeView.getBarcodeView().setDecoderFactory(() -> new MultiFormatReader()) 定制解码格式列表。

  2. 连续扫描与批量处理

    • 在第一次识别后不暂停,累积结果并通过列表展示;

    • 对相同内容去重,避免重复扫描。

  3. CameraX + ML Kit

    • 利用 Google ML Kit 的 BarcodeScanner 获得更高识别率;

    • 可检测光线、自动对焦、文字识别等。

  4. Compose 重写

    • 使用 AndroidViewBinding 嵌入 DecoratedBarcodeView,或纯 CameraX + Compose 的 Box + CameraPreview 结合解码逻辑;

  5. 自定义解码算法

    • 对极端角度、模糊或锚点外二维码进行预处理(灰度、二值化、旋转);

    • 结合 OpenCV 做图像增强。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值