<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          仿微信做個(gè)極速的二維碼掃描功能!

          共 11591字,需瀏覽 24分鐘

           ·

          2021-06-22 18:26


             
           微信改了推動(dòng)機(jī)制,真愛(ài)請(qǐng)星標(biāo)本公號(hào)
          公眾號(hào)回復(fù)加入BATcoder技術(shù)群BAT


          作者:安和橋北的少年 

          https://juejin.cn/user/616168885849976

          正文


          一直為網(wǎng)上找的掃碼項(xiàng)目無(wú)法快速且識(shí)別率高的掃描并解析出二維碼而苦惱,zxing自己修改集成的難度又比較高(菜是原罪)。好在,經(jīng)高人指導(dǎo),接觸到了谷歌的MLkit工程,實(shí)際體驗(yàn)下來(lái),是真的好用,在此做一個(gè)經(jīng)驗(yàn)分享,也是為了自己以后能記住。先來(lái)看看集成之后的效果圖:



          跟微信掃碼比起來(lái),不能說(shuō)是一模一樣,但至少是有點(diǎn)相似了。



          如此迷你的二維碼也能解析出來(lái)哦。接下來(lái),我們來(lái)看下如何具體實(shí)現(xiàn)吧。


          首先,來(lái)看下重中之重,MLKit的項(xiàng)目介紹吧。

          https://developers.google.cn/ml-kit


          可以看到,這個(gè)項(xiàng)目除了掃碼功能,還有其他各種各樣的好用且免費(fèi)的SDK,像是文字識(shí)別,人臉識(shí)別等等,都可以依賴(lài)這個(gè)項(xiàng)目輕松搞定。無(wú)奈小弟才疏學(xué)淺,只接觸了該項(xiàng)目的掃碼功能,等來(lái)日水平提升了,定將其他的功能也一一體驗(yàn)一遍。


          對(duì)了,這個(gè)項(xiàng)目除了Android端,還有IOS端的哦,感興趣的IOS小伙伴可以自行參考

          要使用這個(gè)項(xiàng)目,必不可少的,需要先引入工程。



          dependencies {
                // ...
                // Use this dependency to bundle the model with your app
                implementation 'com.google.mlkit:barcode-scanning:16.1.1'
              }


          有g(shù)oogle play條件的小伙伴,還可以選擇添加google play的相關(guān)引用,甚至可以使用google play model。


          dependencies {
                // ...
                // Use this dependency to use the dynamically downloaded model in Google Play Services
                implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:16.1.4'
              }




          <application ...>
                    ...
                    <meta-data
                        android:name="com.google.mlkit.vision.DEPENDENCIES"
                        android:value="barcode" />
                    <!-- To use multiple models: android:value="barcode,model2,model3" -->
                </application>


          CameraX的支持當(dāng)然也要一并加上。


          // 版本號(hào)
              def camerax_version = "1.0.0-rc03"
              // 對(duì)camera 及 camera2的支持,可自行選擇
              implementation "androidx.camera:camera-core:${camerax_version}"
              implementation "androidx.camera:camera-camera2:${camerax_version}"


          項(xiàng)目使用的是基于jetpack lifecycle的框架,所以加上lifecycle的支持。


              implementation "androidx.camera:camera-lifecycle:${camerax_version}"


          預(yù)覽用到了CameraX自帶的預(yù)覽控件。


              implementation "androidx.camera:camera-view:1.0.0-alpha22"


          接下來(lái)就是具體的代碼實(shí)現(xiàn)了。首先,用到了相機(jī)及相冊(cè),自然需要添加相應(yīng)的權(quán)限申請(qǐng)。


          ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE),REQUEST_PERMISSION)


          先來(lái)看看掃碼界面的實(shí)現(xiàn),主要包含了預(yù)覽的PreviewView及用于繪制掃碼線(xiàn)條及掃碼結(jié)果的ScanOverlay。


          <androidx.camera.view.PreviewView
                  android:id="@+id/previewView"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  />
          <com.hsmedia.mlkitdemo.ScanOverlay
                  android:id="@+id/overlay"
                  android:layout_width="match_parent"
                  android:layout_height="0dp"
                  app:layout_constraintTop_toBottomOf="@id/iv_exit"
                  app:layout_constraintBottom_toTopOf="@id/tv_tips"
                  android:layout_marginBottom="20dp"
                  android:layout_marginTop="20dp"
                  />



          接下來(lái)看一下如何開(kāi)啟CameraX的預(yù)覽。CameraX自帶了檢測(cè)相機(jī)是否可用的監(jiān)聽(tīng),可以在相機(jī)可用之后,再進(jìn)行后續(xù)操作。


          cameraProviderFuture = ProcessCameraProvider.getInstance(this)
          cameraProviderFuture.addListener(Runnable {
              val cameraProvider = cameraProviderFuture.get()
              bindScan(cameraProvider, overlay.width,overlay.height)
          }, ContextCompat.getMainExecutor(this@BarcodeScanningActivity))


          并且,CameraX綁定生命周期控件后,可以根據(jù)生命周期,自行釋放相機(jī),媽媽再也不用擔(dān)心忘記關(guān)相機(jī)啦。


          val preview : Preview = Preview.Builder().build()

          //綁定預(yù)覽
          preview.setSurfaceProvider(previewView.surfaceProvider)

          //使用后置相機(jī)
          val cameraSelector : CameraSelector = CameraSelector.Builder()
              .requireLensFacing(CameraSelector.LENS_FACING_BACK)
              .build()
          //將相機(jī)綁定到當(dāng)前控件的生命周期
          camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)



          這樣,就可以開(kāi)啟預(yù)覽啦,可以注意到,代碼中綁定到生命周期的時(shí)候,使用了一個(gè)imageAnalysis的useCases,而這,就是用于圖片掃描的組件了。


          //配置圖片掃描
          val imageAnalysis = ImageAnalysis.Builder()
              .setTargetResolution(Size(width, height))
              .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
              .build()



          光這樣還不行,還需要為圖片掃描配置解析掃碼內(nèi)容的解析器QRCodeAnalyser,說(shuō)了這么多,終于要用到MLKit的二維碼解析了。


          來(lái)看一下QRCodeAnalyser的具體實(shí)現(xiàn)。


          @SuppressLint("UnsafeExperimentalUsageError")
              override fun analyze(imageProxy: ImageProxy) {
                  val mediaImage = imageProxy.image ?: kotlin.run {
                      imageProxy.close()
                      return
                  }
          val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
          detector.process(image)
                  .addOnSuccessListener { barCodes ->
                      if (barCodes.size > 0){
                          listener.invoke(barCodes[0],imageProxy.width,imageProxy.height)
                          //接收到結(jié)果后,就關(guān)閉解析
                          detector.close()
                      }
                  }
                  .addOnFailureListener { Log.d(TAG, "Error: ${it.message}") }
                  .addOnCompleteListener { imageProxy.close() }

              }



          這個(gè)類(lèi)主要實(shí)現(xiàn)了ImageAnalysis.Analyzer,并實(shí)現(xiàn)了analyze解析方法,其中imageProxy就是CameraX傳遞過(guò)來(lái)的圖片掃描內(nèi)容了。其主要內(nèi)容,就是根據(jù)傳遞過(guò)來(lái)的圖片掃描內(nèi)容,使用detector進(jìn)行解析,而這個(gè)detector又是怎么來(lái)的呢?


           //配置當(dāng)前掃碼格式
          private val options = BarcodeScannerOptions.Builder()
              .setBarcodeFormats(
                  Barcode.FORMAT_QR_CODE,
                  Barcode.FORMAT_AZTEC
              )
              .build()
          //獲取解析器
          private val detector = BarcodeScanning.getClient(options)



          這里的BarcodeScanning就是MLKit提供的二維碼解析組件了。解析完成后,對(duì)掃碼結(jié)果進(jìn)行后續(xù)操作。


          //綁定圖片掃描解析
          imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), QRCodeAnalyser{
              barcode,imageWidth,imageHeight->
              //解綁當(dāng)前所有相機(jī)操作
              cameraProvider.unbindAll()
              //初始化縮放比例
              initScale(imageWidth,imageHeight)
              barcode.boundingBox?.let {//掃描二維碼的外邊框矩形
                  overlay.addRect(translateRect(it))
                  Log.i(TAG, "bindScan: left:${it.left} right:${it.right} top:${it.top} bottom:${it.bottom}")
              }
              Handler().postDelayed({
                  //延遲1S后返回結(jié)果
                  val intent = Intent()
                  intent.putExtra(SCAN_RESULT,barcode.rawValue)
                  setResult(Activity.RESULT_OK, intent)
              },1000)
          })



          這里需要注意的是,因?yàn)槭謾C(jī)上overlay的繪制區(qū)域與實(shí)際的圖片掃描區(qū)域并不是一致的,所以需要對(duì)返回內(nèi)容中二維碼的外邊框矩形進(jìn)行一個(gè)轉(zhuǎn)換,否則,最后繪制的結(jié)果點(diǎn)位置會(huì)出錯(cuò)。


          首先,根據(jù)繪制區(qū)域的大小和圖片掃描區(qū)域的大小,初始化縮放比例。


          private fun initScale(imageWidth : Int, imageHeight : Int){
              if(isPortraitMode(this)){
                  scaleY = overlay.height.toFloat() / imageWidth.toFloat()
                  scaleX = overlay.width.toFloat() / imageHeight.toFloat()
              }else{
                  scaleY = overlay.height.toFloat() / imageHeight.toFloat()
                  scaleX = overlay.width.toFloat() / imageWidth.toFloat()
              }
          }



          這里需要注意的是,因?yàn)閷?shí)際相機(jī)和掃描的角度,在豎屏模式下,有一個(gè)90度的差別,所以這里實(shí)際上做的比例是用繪制區(qū)域的高和掃描區(qū)域的寬,繪制區(qū)域的寬和掃描區(qū)域的高去做一個(gè)計(jì)算。


          private fun translateX(x: Float): Float = x * scaleX
          private fun translateY(y: Float): Float 
          = y * scaleY

          //將掃描的矩形換算為當(dāng)前屏幕大小
          private fun translateRect(rect: Rect) = RectF(
              translateX(rect.left.toFloat()),
              translateY(rect.top.toFloat()),
              translateX(rect.right.toFloat()),
              translateY(rect.bottom.toFloat())
          )



          根據(jù)比例去計(jì)算實(shí)際的二維碼外邊框矩形,并計(jì)算出最后的繪制點(diǎn),進(jìn)行繪制。


          最后的掃碼效果,無(wú)論是掃碼速度,還是解析率,我覺(jué)得都要比之前用的基于zxing的掃碼工程要高,感興趣的小伙伴可以自行嘗試一下。




          ·················END·················

          推薦閱讀

          ? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!

          ? 『BATcoder』做了多年安卓還沒(méi)編譯過(guò)源碼?一個(gè)視頻帶你玩轉(zhuǎn)!

          ? 鴻蒙來(lái)了,拜拜了,Powered by Android!

          ? 重生!進(jìn)階三部曲第一部《Android進(jìn)階之光》第2版 出版!

          BATcoder技術(shù)群,讓一部分人先進(jìn)大廠

          大家,我是劉望舒,騰訊云最具價(jià)值專(zhuān)家TVP,著有暢銷(xiāo)書(shū)《Android進(jìn)階之光》《Android進(jìn)階解密》《Android進(jìn)階指北》,連續(xù)四年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,谷歌開(kāi)發(fā)者社區(qū)特邀講師。

          前華為技術(shù)專(zhuān)家,現(xiàn)大廠技術(shù)負(fù)責(zé)人。

          想要加入 BATcoder技術(shù)群,公號(hào)回復(fù)BAT 即可。

          為了防止失聯(lián),歡迎關(guān)注我的小號(hào)


                 
            微信改了推送機(jī)制,真愛(ài)請(qǐng)星標(biāo)本公號(hào)??
          瀏覽 111
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一本色道久久综合亚洲精品苍井空 | 成人AV高清无码在线观 | 国产久久久久久久 | 久久国产V一级毛多内射 | 正在播放探花系列av |