@TOC
简介
Zxing 是常用的第三方开源 SDK。但是,Zxing存在以下缺陷:仅实现扫描二维码的基本操作,不支持强光、弯曲、变形等更复杂的扫描环境。目前主流做法是基于Zxing对源码进行优化。但是,优化效果仍然不理想,很多人会在优化上花费大量时间。
华为Scan Kit服务提供便捷的条码和二维码扫描、解析、生成能力,帮助开发者快速构建应用中的二维码扫描功能。得益于华为在计算机视觉领域的长期积累,华为统一条码扫描服务(Scan Kit)可检测并自动放大远距离或小尺寸条码,优化识别常见复杂条码扫描场景(如反射等)。 、暗光、污迹、模糊和圆柱体)。提高二维码扫描成功率和用户体验。
设备支持
平台 | 设备 | 版本 |
---|---|---|
Android | 手机和平板 | EMUI 3.1以上、其他Android 4.4及以上 |
iOS | 手机 | iOS 9.0以上 |
集成
如果APP需要在应用商店上架,请先在 AppGallery Connect 中配置应用信息。查看开发指南。
如果应用无上架需求,可以忽略。
配置HMS Core SDK的Maven仓地址
在项目的build.gradle
中配置HMS Core SDK的Maven仓地址。
buildscript {
repositories {
...
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
配置HMS Core
classpath 'com.huawei.agconnect:agcp:1.2.1.301'
}
}
allprojects {
repositories {
...
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
}
在应用级的“build.gradle
中添加依赖包,HMS Core提供了两种类型的依赖包。
implementation 'com.huawei.hms:scanplus:1.3.2.300'
或者
implementation 'com.huawei.hms:scan:1.3.2.300'
并在文件头部添加插件:
apply plugin: 'com.huawei.agconnect'
- 如果您的应用不需要支持其他语言,请跳过此步骤。默认情况下,该应用程序将支持 HMS SDK 支持的所有语言。
- 如果您的应用仅支持某些语言,您可以在此步骤中配置语言。
android {
defaultConfig{
...
resConfigs "en-rGB", "zh-rCN"
}
}
添加权限
在调用模块的AndroidManifest.xml文件中声明二维码扫描页面。
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
配置混淆脚本
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.**{*;}
如果使用过 AndResGuard,请将其添加到混淆脚本文件中的许可名单中。
"R.string.hms*" ,
"R.string.connect_server_fail_prompt_toast" ,
"R.string.getting_message_fail_prompt_toast" ,
"R.string.no_available_network_prompt_toast" ,
"R.string.third_app_*" ,
"R.string.upsdk_*" ,
"R.layout.hms*" ,
"R.layout.upsdk_*" ,
"R.drawable.upsdk*" ,
"R.color.upsdk*" ,
"R.dimen.upsdk*" ,
"R.style.upsdk*" ,
“R.string.agc*”
使用
Scan Kit 有四种模式:
- Default View Mode
- Customized View Mode
- Bitmap Mode
- MultiProcessor Mode
Default View Mode
提供相机扫码和导入图片扫码两个功能,提供完整的Activity,不需要开发者开发扫码界面的UI。
private fun gotoScan() {
val options =
HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.ALL_SCAN_TYPE).create()
ScanUtil.startScan(
this, REQUEST_CODE,
options
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE -> {
val hmsScan: HmsScan? =
data?.getParcelableExtra(ScanUtil.RESULT) // 获取扫码结果 ScanUtil.RESULT
if (!TextUtils.isEmpty(hmsScan?.getOriginalValue())) {
tv_card_id.text = hmsScan?.getOriginalValue()
}
}
}
}
Customized View Mode
支持开发者自定义扫码界面,扫码过程和相机控制将由Scan Kit完成。这种模式更适合对扫码界面有定制化要求的场景。
class CustomizedModeActivity : AppCompatActivity() {
companion object {
const val SCAN_RESULT = "scanResult"
private const val SCAN_FRAME_SIZE = 300
}
private var remoteView: RemoteView? = null
var mScreenWidth = 0
var mScreenHeight = 0
private val mBinding by lazy {
ActivityCustomizedModeBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
//1. 获取屏幕密度计算 viewfinder 的矩形
val dm = resources.displayMetrics
//2. 获取屏幕尺寸
val density = dm.density
mScreenWidth = dm.widthPixels
mScreenHeight = dm.heightPixels
val scanFrameSize = (SCAN_FRAME_SIZE * density)
//3. 计算 viewinder 的矩形,放在布局中央
val rect = Rect()
apply {
rect.left = (mScreenWidth / 2 - scanFrameSize / 2).toInt()
rect.right = (mScreenWidth / 2 + scanFrameSize / 2).toInt()
rect.top = (mScreenHeight / 2 - scanFrameSize / 2).toInt()
rect.bottom = (mScreenHeight / 2 + scanFrameSize / 2).toInt()
}
// 4. 初始化RemoteView, 并且设置回调监听,这里可能设置扫码选项
remoteView = RemoteView.Builder().setContext(this).setBoundingBox(rect)
.setFormat(HmsScan.ALL_SCAN_TYPE).build()
remoteView?.onCreate(savedInstanceState)
remoteView?.setOnResultCallback { result ->
if (result != null && result.isNotEmpty() && result[0] != null && !TextUtils.isEmpty(
result[0].getOriginalValue()
)
) {
val intent = Intent()
intent.apply {
putExtra(SCAN_RESULT, result[0])
}
setResult(Activity.RESULT_OK, intent)
this.finish()
}
}
// 5. 添加 RemoteView 至布局.
val params = FrameLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
mBinding.rim1.addView(remoteView, params)
}
// 6. 管理RemoteView 的生命周期
override fun onStart() {
super.onStart()
remoteView?.onStart()
}
override fun onResume() {
super.onResume()
remoteView?.onResume()
}
override fun onPause() {
super.onPause()
remoteView?.onPause()
}
override fun onDestroy() {
super.onDestroy()
remoteView?.onDestroy()
}
override fun onStop() {
super.onStop()
remoteView?.onStop()
}
}
Bitmap Mode
可以通过相机取帧的方式生成 Bitmap 进行检测,也可以通过图片文件转换成 Bitmap 进行检测等等,能 Bitmap 就能 检测,适用于图片识码等场景。
fun startBitmapMode(view: View) {
EasyPhotos.createAlbum(
this, false, false,
GlideEngine.getInstance()
)
.setFileProviderAuthority(BuildConfig.APPLICATION_ID)
.setCount(1)
.start(object : SelectCallback() {
override fun onResult(photos: ArrayList<Photo>?, isOriginal: Boolean) {
photos?.let {
val path = photos.first().path
if (TextUtils.isEmpty(path)) {
return
}
// 1. 转换为 Bitmap
val bitmap = ScanUtil.compressBitmap(this@MainActivity, path)
// 2. 调用 decodeWithBitmap 方法识别 Bitmap.
val result = ScanUtil.decodeWithBitmap(
this@MainActivity,
bitmap,
HmsScanAnalyzerOptions.Creator().setHmsScanTypes(0).setPhotoMode(false)
.create()
)
// 3. 显示识别结果
if (result != null && result.isNotEmpty()) {
if (!TextUtils.isEmpty(result[0].getOriginalValue())) {
mBinding.tvResult.text = result[0].getOriginalValue()
}
}
}
}
override fun onCancel() {
Toast.makeText(
this@MainActivity,
"图片选取失败",
Toast.LENGTH_SHORT
)
.show()
}
})
}
MultiProcessor Mode
相机扫码、导入图片扫码,支持同时检测多个码,支持同步和异步两种方式。
fun startMultiProcessorMode(view: View) {
EasyPhotos.createAlbum(
this, false, false,
GlideEngine.getInstance()
)
.setFileProviderAuthority(BuildConfig.APPLICATION_ID)
.setCount(1)
.start(object : SelectCallback() {
override fun onResult(photos: ArrayList<Photo>?, isOriginal: Boolean) {
photos?.let {
val path = photos.first().path
if (TextUtils.isEmpty(path)) {
return
}
// 1. 转换为 Bitmap
val bitmap = ScanUtil.compressBitmap(this@MainActivity, path)
// 2. 配置可选项
val options =
HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.ALL_SCAN_TYPE)
.create()
// 3. 初始化 HmsScanAnalyzer 对象
val scanAnalyzer = HmsScanAnalyzer(options)
// 4. 构建 MLFrame
val image = MLFrame.fromBitmap(bitmap)
// 5. 扫码识别
/* 同步模式
val result: SparseArray<HmsScan> = scanAnalyzer.analyseFrame(image)
Log.d(TAG, result.toString())
*/
// 异步模式
scanAnalyzer.analyzInAsyn(image).addOnSuccessListener {
if (it != null && it.size > 0) {
var resultStr = ""
it.forEach { value ->
resultStr = resultStr.plus(value.originalValue).plus("\n")
}
mBinding.tvResult.text = resultStr
}
}.addOnFailureListener {
it?.printStackTrace()
Log.d(TAG, it.message ?: "")
}
}
}
override fun onCancel() {
Toast.makeText(
this@MainActivity,
"图片选取失败",
Toast.LENGTH_SHORT
)
.show()
}
})
}