一、项目介绍
1.1 背景与意义
二维码(QR Code)作为一种二维条码,因其存储容量大、识别速度快、容错能力强,被广泛用于商品追溯、电子票务、移动支付、身份验证等场景。在 Android 应用中集成二维码扫描功能,能够帮助用户快速读取二维码中的信息,如 URL、文本、联系方式、Wi‑Fi 配置等,大大提升用户体验。
本项目旨在从零开始演示如何在 Android 应用中实现二维码扫描功能,并深入探讨其背后的原理、关键技术与最佳实践。文章将涵盖以下内容:
-
相关技术概览:二维码原理、常用开源库对比;
-
项目架构与依赖:如何选择相机 API 与扫描库;
-
核心实现思路:相机预览、图像格式转换、异步解码;
-
完整代码整合:
Activity
、自定义View
、布局 XML、权限申请、回调接口全部集中展示; -
代码详解:剖析每个方法的功能与关键点;
-
性能与兼容性优化:预览分辨率、线程调度、权限流程、Android 6.0+ 运行时申请;
-
拓展方向:连续扫描、识别条形码、嵌入 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 | 优点 | 缺点 |
---|---|---|---|
ZXing | zxing/zxing | 成熟稳定、支持多种条码类型 | 原生 Java 解码性能一般,UI 较陈旧 |
ZBar | ZBar/ZBar | C 语言实现,速度快 | 集成略繁琐,滤镜识别需自行配置 |
Google ML Kit | - | 谷歌官方、后续维护、支持多种 ML 功能 | 需联网或下载模型,体积较大 |
ZXing Android Embedded | journeyapps/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
-
布局中添加
DecoratedBarcodeView
:它封装了预览、解码、激光线等 UI。 -
通过
barcodeView.decodeContinuous(callback)
:启动连续解码模式。 -
在
BarcodeCallback
中获取解码结果:在回调内更新 UI 或启动下一个 Activity。 -
生命周期管理:在
onResume()
调用barcodeView.resume()
;在onPause()
调用barcodeView.pause()
。
4.2 原生 CameraX + ML Kit
-
CameraX Preview:通过
PreviewView
和ProcessCameraProvider
获取预览。 -
ImageAnalysis:创建
ImageAnalysis
用以拿到相机帧,设置合适的分辨率与回调线程。 -
ML Kit 扫码:在
analyzer
中使用BarcodeScanning.getClient()
对InputImage.fromMediaImage()
进行检测。 -
结果回调:在主线程更新扫描结果。
五、完整代码整合
<!-- ==================== 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
做进一步定制。
六、核心代码解读
-
DecoratedBarcodeView
-
封装了扫码预览(CameraPreview)、扫码框(ViewfinderView)、激光线、提示文本等 UI;
-
decodeContinuous(BarcodeCallback)
启动连续解码,回调在任意线程;
-
-
BarcodeCallback.barcodeResult(...)
-
当 ZXing 成功识别到一帧二维码时回调,返回
BarcodeResult
; -
推荐在首次识别后调用
barcodeView.pause()
,避免重复识别;
-
-
权限管理
-
使用
ActivityResultContracts.RequestPermission
API 动态请求 CAMERA 权限; -
若被拒绝,应提示用户并退出或禁用扫码功能;
-
-
生命周期管理
-
在
onResume()
调用barcodeView.resume()
,恢复相机与解码线程; -
在
onPause()
调用barcodeView.pause()
,释放相机资源;
-
七、性能与兼容性优化
-
预览分辨率
-
默认使用相机最高可用分辨率,可能影响解码速度;
-
可通过
barcodeView.getBarcodeView().getCameraSettings().setRequestedCameraResolution(...)
设置合适尺寸,如 1280×720;
-
-
线程调度
-
ZXing Embedded 已在后台执行解码,无需手动管理;
-
若改用 CameraX + ML Kit,需在
ImageAnalysis
的setAnalyzer(Executors.newSingleThreadExecutor(), analyzer)
中指定单线程解码,避免 UI 线程阻塞。
-
-
闪光灯控制
-
若光线不足,可在 UI 中显示“打开/关闭闪光灯”按钮;
-
使用
barcodeView.setTorchOn()
/setTorchOff()
;
-
-
平滑 UX
-
在扫码框周围显示半透明遮罩,突出扫码区域;
-
使用动画提示用户对准二维码;
-
八、扩展方向
-
条码/二维码混合识别
-
ZXing 支持多种格式:EAN‑13、Code 39、PDF417、Data Matrix 等;
-
通过
barcodeView.getBarcodeView().setDecoderFactory(() -> new MultiFormatReader())
定制解码格式列表。
-
-
连续扫描与批量处理
-
在第一次识别后不暂停,累积结果并通过列表展示;
-
对相同内容去重,避免重复扫描。
-
-
CameraX + ML Kit
-
利用 Google ML Kit 的
BarcodeScanner
获得更高识别率; -
可检测光线、自动对焦、文字识别等。
-
-
Compose 重写
-
使用
AndroidViewBinding
嵌入DecoratedBarcodeView
,或纯 CameraX + Compose 的Box
+CameraPreview
结合解码逻辑;
-
-
自定义解码算法
-
对极端角度、模糊或锚点外二维码进行预处理(灰度、二值化、旋转);
-
结合 OpenCV 做图像增强。
-