炫酷!讓Android同時掃多個二維碼的魔法揭秘
想象一下:超市里別人還在一個一個掃商品二維碼,你的APP卻能"唰"一下瞬間識別整排商品!今天教大家用Google的黑科技MLKit+CameraX,輕松實現(xiàn)這個超酷功能。別擔心,跟著做絕對能搞定~
準備工作:裝備你的"魔法棒"
// build.gradle 添加這些"魔法材料"
dependencies {
implementation 'androidx.camera:camera-camera2:1.3.1' // 相機核心
implementation 'androidx.camera:camera-lifecycle:1.3.1' // 生命周期管家
implementation 'androidx.camera:camera-view:1.3.1' // 取景器
implementation 'com.google.mlkit:barcode-scanning:17.1.0' // 二維碼識別引擎
}<!-- AndroidManifest.xml 申請相機權限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />?? 就像哈利波特需要魔杖,這些就是我們的"魔法裝備"!記得先在手機設置里開啟相機權限哦~
搭建舞臺:創(chuàng)建掃描界面
<!-- activity_main.xml 布置舞臺 -->
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />?? 這個全屏"魔法鏡"就是我們的掃描窗口,用戶看到的實時畫面都在這里顯示
核心魔法:二維碼識別器
class QRCodeAnalyzer(privateval onDetect: (List<Barcode>) -> Unit) : ImageAnalysis.Analyzer {
// 設置只識別二維碼(避免誤認條形碼)
privateval options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
privateval scanner = BarcodeScanning.getClient(options) // 創(chuàng)建識別器實例
@SuppressLint("UnsafeExperimentalUsageError")
overridefun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
mediaImage?.let {
// 將相機畫面轉(zhuǎn)為可識別格式
val image = InputImage.fromMediaImage(it, imageProxy.imageInfo.rotationDegrees)
scanImage(image, imageProxy) // 開始掃描!
}
}
privatefun scanImage(image: InputImage, imageProxy: ImageProxy) {
scanner.process(image)
.addOnSuccessListener { codes ->
onDetect(codes) // 成功抓到所有二維碼!
}
.addOnCompleteListener {
imageProxy.close() // 關閉當前幀,準備下一幀
}
}
}?? 這段代碼就像訓練了一只"二維碼獵犬":
? analyze()負責轉(zhuǎn)換相機畫面格式
? scanImage()釋放獵犬識別二維碼
? 識別完成后自動重置準備下次狩獵
啟動魔法:把一切組裝起來
class MainActivity : AppCompatActivity() {
private val cameraExecutor = Executors.newSingleThreadExecutor() // 專用工作線程
private val viewFinder by lazy { findViewById<PreviewView>(R.id.viewFinder) }
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 創(chuàng)建預覽畫面
val preview = Preview.Builder().build().apply {
setSurfaceProvider(viewFinder.surfaceProvider)
}
// 創(chuàng)建二維碼識別管道
val qrAnalyzer = ImageAnalysis.Builder().build().apply {
setAnalyzer(cameraExecutor, QRCodeAnalyzer { codes ->
// 識別結果回調(diào)區(qū) ▼
codes.forEachIndexed { index, code ->
Log.d("QR_DEBUG", "抓到二維碼 ${index + 1}: ${code.rawValue}")
}
})
}
try {
// 組裝所有部件!啟動!
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this,
CameraSelector.DEFAULT_BACK_CAMERA, // 使用后置攝像頭
preview,
qrAnalyzer
)
} catch (e: Exception) {
Log.e("CAMERA", "啟動失敗", e)
}
}, ContextCompat.getMainExecutor(this))
}
}啟動流程四步走:
? 獲取相機控制權
? 設置預覽窗口
? 連接二維碼識別器
? 啟動整個系統(tǒng)!
圖片
圖片
讓掃描結果躍然屏上
想要把掃描到的二維碼信息實時展示出來?小菜一碟!
<!-- 結果展示層 -->
<LinearLayout
android:id="@+id/resultsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical"
android:background="#80000000"
android:padding="16dp">
<TextView
android:id="@+id/tvHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="掃描結果"
android:textColor="#4CAF50"
android:textSize="18sp"
android:textStyle="bold"/>
<LinearLayout
android:id="@+id/resultsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="8dp"/>
</LinearLayout>設計思路:動態(tài)添加識別到的每個二維碼信息
在MainActivity中添加處理邏輯:
// 獲取布局元素引用
private val resultsLayout by lazy { findViewById<LinearLayout>(R.id.resultsLayout) }
private val resultsContainer by lazy { findViewById<LinearLayout>(R.id.resultsContainer) }
// 顯示掃描結果的核心方法
private fun showScanResults(codes: List<Barcode>) {
// 清空之前的結果
resultsLayout.removeAllViews()
if (codes.isEmpty()) {
// 沒有掃描到結果時顯示提示
val emptyView = TextView(this).apply {
text = "???♀? 正在尋找二維碼..."
setTextColor(Color.WHITE)
}
resultsLayout.addView(emptyView)
return
}
// 動態(tài)添加每個二維碼結果
codes.forEachIndexed { index, barcode ->
val resultView = createResultView(barcode, index)
resultsLayout.addView(resultView)
}
}
// 創(chuàng)建單個結果視圖
private fun createResultView(barcode: Barcode, index: Int): TextView {
return TextView(this).apply {
// 解析二維碼內(nèi)容
val content = barcode.rawValue ?: "未知內(nèi)容"
// 格式化顯示文本
text = "? 二維碼 ${index + 1}:\n${content.take(50)}${if (content.length > 50) "..." else ""}"
setTextColor(Color.WHITE)
setTypeface(null, Typeface.BOLD)
textSize = 14f
// 添加點擊事件查看完整內(nèi)容
setOnClickListener {
AlertDialog.Builder(this@MainActivity)
.setTitle("二維碼詳情")
.setMessage(content)
.setPositiveButton("復制") { _, _ ->
copyToClipboard(content)
Toast.makeText(this@MainActivity, "已復制到剪貼板", Toast.LENGTH_SHORT).show()
}
.setNegativeButton("關閉", null)
.show()
}
}
}
// 復制到剪貼板工具方法
private fun copyToClipboard(text: String) {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("二維碼內(nèi)容", text)
clipboard.setPrimaryClip(clip)
}
圖片
超實用小技巧
? 性能優(yōu)化:在ImageAnalysis.Builder()后加上.setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST)避免卡頓
? 多類型支持:修改.setBarcodeFormats()可同時識別條形碼/二維碼
? 聚焦區(qū)域:添加viewFinder.setOnTouchListener實現(xiàn)點擊聚焦
// 添加點擊聚焦功能
viewFinder.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
val factory = viewFinder.meteringPointFactory
val point = factory.createPoint(event.x, event.y)
CameraControl?.startFocusAndMetering(FocusMeteringAction.Builder(point).build())
}
true
}CameraX搭舞臺,ML Kit來識別,異步處理不卡頓,多碼掃描So Easy!
現(xiàn)在你的APP已經(jīng)擁有"火眼金睛"啦!快去試試同時掃描一排二維碼的爽快感吧~
源碼https://github.com/Reathin/Sample-Android/tree/master/module_mlkit_barcode_scanning


























