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