H5获取手机相机或相册图片两种方式-Android通过webview通报多张照片给H5 ...

铁佛  金牌会员 | 2024-6-11 13:01:09 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 663|帖子 663|积分 1989

需求目的: 手机机通过webView展示H5网页,在特殊场景下,需要使用相机照相或者从相册获取照片,上传背景。
完备流程效果: 如下图

一、H5界面样例代码

使用html文件格式,文件直接打开就可以展示布局;一会在andriod webview中直接加载
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Document</title>
  7. </head>
  8. <div id="app">
  9.     <h1>alllalalallalal 默认会被覆盖</h1>
  10. </div>
  11. <template id="why">
  12.     <div>
  13.         <h2>{{message}}</h2>
  14.         <h2>{{counter}}</h2>
  15.         <button @click="increment">+1</button>
  16.         <button @click="decrement">-1</button>
  17.         <h1 style="text-align: center;">{{ title }}</h1>
  18.         <div>
  19.             <h2 style="text-align: center;">android选中照片H5展示</h2>
  20.             <!--HTML5提供了<input type="file">元素来实现选取文件的功能,在WebView表现为调用onShowFileChooser-->
  21.             <input accept="image/*" capture="camera" ref="imgFile" type="file" multiple
  22.                    @change="previewFiles">
  23.             <div id="preview">
  24.                 <img v-for="imgSrc in imageSources" :src="imgSrc" :key="imgSrc"
  25.                      style="max-width: 100px; max-height: 100px; margin: 10px;">
  26.             </div>
  27.         </div>
  28.     </div>
  29. </template>
  30. <body>
  31. <!-- 引入 Vue 3 的 CDN 资源网络加载不了 -->
  32. <!-- <script src="https://unpkg.com/vue@next"></script> -->
  33. <!-- 引入 Vue 3 的 CDN 资源,本地引用 -->
  34. <script src="vue3.2.12global.js"></script>
  35. <script>
  36.         Vue.createApp({
  37.             template: '#why',
  38.             data: function () {
  39.                 return {
  40.                     message: "功能开发中,敬请期待!",
  41.                     counter: 100,
  42.                     pictureSelectorContent: "相机选择结果:",
  43.                     imageSources: [] // 存储图片的数据URL
  44.                 }
  45.             },
  46.             // 在你的 Vue 组件中处理 Webview 传递的数据
  47.             mounted() {
  48.                 // 设置全局函数,用于接收 WebView 调用
  49.                 // window.pictureSelectorResult = this.pictureSelectorResult;
  50.             },
  51.             methods: {
  52.                 increment() {
  53.                     this.counter++;
  54.                     console.log("点击了+1");
  55.                 },
  56.                 decrement() {
  57.                     this.counter--;
  58.                     consloe.log("点击了-1");
  59.                 },
  60.                 startPictureSelector() {
  61.                     window.android.startPictureSelector();
  62.                 },
  63.                 previewFiles() {
  64.                     const files = this.$refs.imgFile.files;
  65.                     this.imageSources = [];
  66.                     for (let i = 0; i < files.length; i++) {
  67.                         const file = files[i];
  68.                         const reader = new FileReader();
  69.                         reader.onload = (e) => {
  70.                             this.imageSources.push(e.target.result);
  71.                         };
  72.                         reader.readAsDataURL(file);
  73.                     }
  74.                 },
  75.             },
  76.         }).mount("#app")
  77. </script>
  78. <style>
  79.         h1 {
  80.             font-size: 80px;
  81.             font-weight: bold;
  82.             margin-bottom: 20px;
  83.         }
  84.         h2 {
  85.             font-size: 20px;
  86.             font-weight: bold;
  87.             color: #C8EFD4;
  88.         }
  89.         h3 {
  90.             font-size: 10px;
  91.             font-weight: bold;
  92.             color: #C8EFD4;
  93.         }
  94.         button1 {
  95.             font-size: 60px;
  96.             padding: 10px 20px;
  97.             background-color: #007bff;
  98.             color: #fff;
  99.             border: none;
  100.             border-radius: 4px;
  101.             cursor: pointer;
  102.             margin-bottom: 20px;
  103.             margin-top: 20px;
  104.             text-align: center;
  105.             /* 将文字水平居中显示 */
  106.             display: flex;
  107.             /* 将按钮设置为flex容器 */
  108.             align-items: center;
  109.             /* 将文字在垂直方向上居中显示 */
  110.         }
  111. </style>
  112. </body>
  113. </html>
复制代码
上述代码是前端代码,使用vue3框架展示一个基础 加减demo界面(不紧张的冗余),以及 一个打开文件的按钮以及展示图片

此中HTML5提供了元素来实现选取文件的功能,当在WebView表现为调用onShowFileChooser后,回调图片uri列表一一获取并展示
二、Android打开相机以及相册的两种方式

方式一:android 原生方式

现实效果和流程示图

1.android界面逻辑代码
这边使用的是kotlin语言,使用的fragment界面展示,使用binding加载了布局,以及声明白webview组件,在webview上导入html链接,使当地H5界面展示
  1. class VisitorFragment : Fragment() {
  2.     private lateinit var binding: FragmentVisitorBinding
  3.     lateinit var mActivity: Activity
  4.     private lateinit var mRoot: View
  5.     companion object {
  6.         const val TAG = "VisitorFragment"
  7.     }
  8.     private var mWebViewUrl: String = "file:///android_asset/vue_android_demo.html"
  9.     var mAppName = MainApplication.instance.configuration.BASE_APP_LOGIN_IDENTITY
  10.     var mSystemName = WebViewConstant.DEFAULT_SYSTEM_NAME
  11.     private var mVisitorAndroidJs: VisitorAndroidJs = VisitorAndroidJs(this)
  12.     private var mWebView: WebView? = null
  13.     var mAndroidId: String = WebViewConstant.DEFAULT_DEVICE_SIGN
  14.     var mApiKey: String = WebViewConstant.DEFAULT_API_KEY
  15.     //回传H5时使用的对象
  16.     private var mUploadCallback: ValueCallback<Array<Uri>>? = null
  17.     //拍照传递的路径uri
  18.     private var mImageUri: Uri? = null
  19.     /**
  20.      * onCreate方法是Activity生命周期的第一个回调方法
  21.      * ,当Activity被创建时被调用。
  22.      * 在这个方法中,你可以进行一些初始化的操作,比如设置布局、绑定控件、初始化数据等。
  23.      *
  24.      * @param savedInstanceState If the fragment is being re-created from
  25.      * a previous saved state, this is the state.
  26.      */
  27.     override fun onCreate(savedInstanceState: Bundle?) {
  28.         super.onCreate(savedInstanceState)
  29.         binding = FragmentVisitorBinding.inflate(layoutInflater)
  30.         mRoot = binding.root
  31.         //记录
  32.         val viewModel = ViewModelProvider(requireActivity())[DashboardViewModel::class.java]
  33.         viewModel.setFragment(this)
  34.         LogUtils.d(TAG, "onCreate")
  35.     }
  36.     /**
  37.      * onCreateView方法是Fragment生命周期的回调方法,
  38.      * 当Fragment创建并绘制其用户界面时被调用。
  39.      * 在这个方法中,你可以通过返回一个View对象来定义Fragment的用户界面。
  40.      * 它常用于加载布局文件、查找和初始化控件等操作。
  41.      *
  42.      * @return
  43.      */
  44.     override fun onCreateView(
  45.         inflater: LayoutInflater,
  46.         container: ViewGroup?,
  47.         savedInstanceState: Bundle?
  48.     ): View {
  49.         LogUtils.d(TAG, "onCreateView")
  50.         initView(mRoot, layoutInflater, null)
  51.         return mRoot
  52.     }
  53.     fun initView(parent: View?, inflater: LayoutInflater?, container: ViewGroup?) {
  54.         //设置当前fragment
  55.         mAndroidId = DeviceUtils.getUniqueId(mActivity)
  56.         initWebView()
  57.         mWebView?.loadUrl(mWebViewUrl)
  58.         //弹出展示链接
  59.         showToast("${getString(R.string.current_develop_environment)}$mWebViewUrl")
  60.         initData()
  61.     }
  62.     override fun onAttach(context: Context) {
  63.         super.onAttach(context)
  64.         mActivity = context as Activity
  65.     }
  66.     /**
  67.      * @param msg 内容
  68.      */
  69.     fun showToast(msg: String?) {
  70.         val activity: Activity? = activity
  71.         activity?.runOnUiThread {
  72.             Toast.makeText(
  73.                 activity,
  74.                 msg,
  75.                 Toast.LENGTH_SHORT
  76.             ).show()
  77.         }
  78.     }
  79.     private fun initData() {
  80.     }
  81.     override fun onResume() {
  82.         super.onResume()
  83.         LogUtils.d(TAG, "onResume")
  84.     }
  85.     @SuppressLint("SetJavaScriptEnabled")
  86.     private fun initWebView() {
  87.         LogUtils.d(TAG, "initWebView")
  88.         mWebView = binding.mainWebView
  89.         mWebView?.requestFocus()
  90.         mWebView?.isHorizontalScrollBarEnabled = false
  91.         mWebView?.isVerticalScrollBarEnabled = false
  92.         val setting = mWebView?.settings
  93.         setting?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;
  94.         setting?.javaScriptEnabled = true
  95.         //用于开启或禁用其 DOM(文档对象模型)存储功能,浏览器缓存这些数据
  96.         setting?.domStorageEnabled = true
  97.         //允许访问文件,默认允许
  98.         setting?.allowFileAccess = true
  99.         //设置不,会引起webView重新不急,默认NARROW_COLUMNS
  100.         setting?.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS
  101.         //自动缩放
  102.         setting?.setSupportZoom(true)
  103.         setting?.builtInZoomControls = true
  104.         //自适应屏幕
  105.         setting?.useWideViewPort = true
  106.         setting?.loadWithOverviewMode = true
  107.         //支持多窗口
  108.         setting?.setSupportMultipleWindows(true)
  109.         setting?.setAppCacheEnabled(true)
  110.         setting?.domStorageEnabled = true
  111.         //定位
  112.         setting?.setGeolocationEnabled(true)
  113.         //优先使用缓存数据,在缓存数据不存在的情况下才去获取网络数据
  114.         setting?.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
  115.         setting?.savePassword = false
  116.         //设置js接口
  117.         mVisitorAndroidJs.let { mWebView?.addJavascriptInterface(it, "android") }
  118.         //页面不跳转浏览器
  119.         mWebView?.webViewClient = object : WebViewClient() {
  120.             override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
  121.                 LogUtils.d(TAG, "url: $url")
  122.                 view.loadUrl(url)
  123.                 return true
  124.             }
  125.             override fun shouldInterceptRequest(
  126.                 view: WebView,
  127.                 request: WebResourceRequest
  128.             ): WebResourceResponse? {
  129.                 var uri = request.url
  130.                 var path = uri.path
  131.                 LogUtils.d(TAG, "uri: $uri, path: $path")
  132.                 return super.shouldInterceptRequest(view, request)
  133.             }
  134.         }
  135.         //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  136.         mWebView?.webChromeClient = object : WebChromeClient() {
  137.             //API >=21(android 5.0.1)回调此方法
  138.             override fun onShowFileChooser(
  139.                 webView: WebView?,
  140.                 filePathCallback: ValueCallback<Array<Uri>>?,
  141.                 fileChooserParams: FileChooserParams?
  142.             ): Boolean {
  143.                 mUploadCallback = filePathCallback
  144.                 //使用拍照或者打开文件
  145.                 mImageUri = ChoosePhotoFile.takePhoto(mActivity)
  146.                 //设置false会IllegalStateException: Duplicate showFileChooser result
  147.                 return true
  148.             }
  149.         }
  150.     }
  151.     fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {
  152.         LogUtils.d(
  153.             TAG,
  154.             "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"
  155.         )
  156.         // 扫描二维码/条码回传
  157.         if (requestCode == ScanCodeUtils.REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {
  158.             LogUtils.d(TAG, "onActivityResultResponse ${intent?.extras}")
  159.             if (intent == null) {
  160.                 //弹出展示链接
  161.                 showToast("扫描结果为空")
  162.                 return
  163.             }
  164.             //传递给js,格式是"scanCodeResult('${data.extras}')",其中单引号很重要,可能导致js script error
  165.             //codedContent是组件中定义的参数名
  166.             setEvaluateJavascript("scanCodeResult('${intent.getStringExtra("codedContent")}')")
  167.         } else if (requestCode == ChoosePhotoFile.REQUEST_CODE) {
  168.             //拍照,界面跳回后,结果文件的使用
  169.             ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)
  170.         }
  171.     }
  172.     @Override
  173.     override fun onDestroy() {
  174.         //防止更新dialog内存泄漏
  175.         super.onDestroy()
  176.     }
  177.     /**
  178.      * 登录成功后跳转
  179.      */
  180.     open fun loginSuccessJump() {
  181.     }
  182.     /**
  183.      * 给网页传值
  184.      * 传递给js,格式是"scanCodeResult('${data.extras}')"
  185.      * 其中单引号很重要,可能导致js script error
  186.      */
  187.     private fun setEvaluateJavascript(jspMethodAndValue: String) {
  188.         LogUtils.d(
  189.             TAG,
  190.             "setEvaluateJavascript mWebView ${mWebView}, jspMethodAndValue $jspMethodAndValue"
  191.         )
  192.         mWebView?.evaluateJavascript(jspMethodAndValue, ValueCallback<String>() {
  193.             LogUtils.d(TAG, "给网页传值为: $jspMethodAndValue")
  194.         })
  195.     }
  196. }
复制代码
android layout布局
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent">
  5.     <WebView
  6.         android:id="@+id/main_web_view"
  7.         android:layout_width="match_parent"
  8.         android:layout_height="match_parent" />
  9. </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
2.获取照片的主要思路是两个方法:


  • webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  1.         //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  2.         mWebView?.webChromeClient = object : WebChromeClient() {
  3.             //API >=21(android 5.0.1)回调此方法
  4.             override fun onShowFileChooser(
  5.                 webView: WebView?,
  6.                 filePathCallback: ValueCallback<Array<Uri>>?,
  7.                 fileChooserParams: FileChooserParams?
  8.             ): Boolean {
  9.                 mUploadCallback = filePathCallback
  10.                 //使用拍照或者打开文件
  11.                 mImageUri = ChoosePhotoFile.takePhoto(mActivity)
  12.                 //设置false会IllegalStateException: Duplicate showFileChooser result
  13.                 return true
  14.             }
  15.         }
复制代码


  • 照相或者相册选中后界面跳回后,使用ValueCallback<Array>回传
  1.     fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {
  2.         LogUtils.d(
  3.             TAG,
  4.             "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"
  5.         )
  6.                  if (requestCode == ChoosePhotoFile.REQUEST_CODE) {
  7.             //拍照或者相册选中后界面跳回后,结果文件的使用
  8.             ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)
  9.         }
  10.     }
复制代码


  • 以上onActivityResultResponse方法需要在actvity onActivityResult方法中使用
    (由于我这里是activity嵌套fragment的,如果直接在activity使用webview就不需我这太贫苦)
  1.     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  2.         super.onActivityResult(requestCode, resultCode, data)
  3.         LogUtils.d(
  4.             TAG,
  5.             "onActivityResult requestCode11 $requestCode, resultCode:$resultCode"
  6.         )
  7.         //设置当前fragment
  8.         val fragment = mDashboardViewModel.fragment.value
  9.         Log.d(TAG, "fragment: $fragment")
  10.         Log.d(TAG, "fragment.isResumed: ${fragment?.isResumed}")
  11.         //界面返回时VisitorFragment还没有Resumed
  12.         if (fragment is VisitorFragment) {
  13.             val visitorFragment: VisitorFragment = fragment
  14.             visitorFragment.onActivityResultResponse(requestCode, resultCode, data)
  15.         }
  16.     }
复制代码
3.打开相机和相册的工具类
  1. object ChoosePhotoFile {
  2.     private const val TAG = "ChoosePhotoFile"
  3.     const val REQUEST_CODE: Int = 12345
  4.     fun takePhoto(activity: Activity): Uri {
  5.         //相机可以访问的公共位置才能存储,获取时需要读取文件权限
  6.         val file: String =
  7.             Environment.getExternalStorageDirectory()
  8.                 .toString() + File.separator + Environment.DIRECTORY_PICTURES + File.separator
  9.         val fileName = "Image_${SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())}.jpg"
  10.         val realFile = File(file, fileName)
  11.         val imageUri = Uri.fromFile(realFile)
  12.         LogUtils.d(TAG, "realFile:$realFile, imageUri: $imageUri")
  13.         // 拍照后获取图片需要文件权限,界面跳转直接拿文件不需要权限
  14.         //检查申请读文件权限
  15.         CheckPermissionUtils.requestPermissions(
  16.             activity,
  17.             arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
  18.         )
  19.         //调起相机,拍一张照片
  20.         val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
  21.         captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
  22.         //调起相册,取一张照片
  23.         val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
  24.         //选择方式,拍照或者相册
  25.         val chooserIntent = Intent.createChooser(photoIntent, "Image Chooser")
  26.         chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf<Parcelable>(captureIntent))
  27.         activity.startActivityForResult(chooserIntent, REQUEST_CODE)
  28.         return imageUri
  29.     }
  30.     /**
  31.      * H5针对从拍照或者相册中选中的图片做处理
  32.      * @param imageUri 拍照返回的数据,
  33.      */
  34.     fun takeActivityResult(
  35.         requestCode: Int,
  36.         intent: Intent?,
  37.         filePathCallback: ValueCallback<Array<Uri>>?,
  38.         imageUri: Uri?
  39.     ) {
  40.         if (requestCode == REQUEST_CODE) {
  41.             //从相册获取,返回的intent
  42.             if (intent != null && intent.data != null) {
  43.                 var uri: Uri = intent.data as Uri
  44.                 LogUtils.d(TAG, "file uri: $uri")
  45.                 filePathCallback?.onReceiveValue(arrayOf<Uri>(uri))
  46.             } else {
  47.                 //从拍照中获取图片,已经返回的imageUri
  48.                 LogUtils.d(TAG, "take photo imageUri: $imageUri")
  49.                 if (imageUri != null) {
  50.                     filePathCallback?.onReceiveValue(arrayOf<Uri>(imageUri))
  51.                 } else {
  52.                     filePathCallback?.onReceiveValue(null)
  53.                 }
  54.             }
  55.         }
  56.     }
  57. }
复制代码
方式二:使用android 组件库是实现-朋侪圈获取照片功能

现实效果和流程示图

1.获取照片的主要思路是两个方法-替换


  • webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  1.         //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  2.         mWebView?.webChromeClient = object : WebChromeClient() {
  3.             //API >=21(android 5.0.1)回调此方法
  4.             override fun onShowFileChooser(
  5.                 webView: WebView?,
  6.                 filePathCallback: ValueCallback<Array<Uri>>?,
  7.                 fileChooserParams: FileChooserParams?
  8.             ): Boolean {
  9.                 mUploadCallback = filePathCallback
  10.                 //使用拍照或者打开文件
  11. //                mImageUri = ChoosePhotoFile.takePhoto(mActivity)
  12.                 //模拟微信朋友圈获取照片模式
  13.                 LogUtils.d(TAG,"onShowFileChooser")
  14.                 PictureSelectorUtils.startPictureSelector(mActivity)
  15.                 //设置false会IllegalStateException: Duplicate showFileChooser result
  16.                 return true
  17.             }
  18.         }
复制代码


  • 照相或者相册选中后界面跳回后,使用ValueCallback<Array>回传
  1.     fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {
  2.         LogUtils.d(
  3.             TAG,
  4.             "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"
  5.         )
  6.                  if (requestCode == PictureSelectorUtils.REQUEST_PICTURE_SELECTOR) {
  7.             LogUtils.d(TAG, "onActivityResultResponse REQUEST_PICTURE_SELECTOR")
  8.             PictureSelectorUtils.takeActivityResult(requestCode, intent, mUploadCallback)
  9.         }
  10.     }
复制代码
2.打开相机和相册的工具类
  1. object PictureSelectorUtils {
  2.     const val REQUEST_PICTURE_SELECTOR = 10012
  3.     const val TAG = "PictureSelectorUtils"
  4.     fun startPictureSelector(activity: Activity) {
  5.         LogUtils.d(TAG, "startPictureSelector")
  6.         // 拍照后获取图片需要文件权限,界面跳转直接拿文件不需要权限
  7.         //检查申请读文件权限
  8. //        CheckPermissionUtils.requestPermissions(
  9. //            activity,
  10. //            arrayOf(
  11. //                Manifest.permission.CAMERA,
  12. //                Manifest.permission.READ_EXTERNAL_STORAGE,
  13. //                Manifest.permission.WRITE_EXTERNAL_STORAGE
  14. //            )
  15. //        )
  16.         //插件里自带了静态权限以及权限校验
  17.         PictureSelector.create(activity).openGallery(PictureMimeType.ofImage())
  18.             .imageEngine(GlideEngine) // Please refer to the Demo GlideEngine.java
  19.             .isWeChatStyle(true) // 是否开启微信图片选择风格
  20.             .selectionMode(PictureConfig.MULTIPLE).forResult(REQUEST_PICTURE_SELECTOR)
  21.     }
  22.     fun getPictures(data: Intent): MutableList<Uri> {
  23.         val selectList = PictureSelector.obtainMultipleResult(data)
  24.         LogUtils.d(TAG, "getPicture selectList: $selectList")
  25.         // 将照片路径转换成 Uri 列表
  26.         val imageUris: MutableList<Uri> = ArrayList()
  27.         if (selectList.isEmpty()) {
  28.             LogUtils.d(TAG, "getPicture selectList isEmpty")
  29.             return imageUris
  30.         }
  31.         for (imagePath in selectList) {
  32.             var path = imagePath.path
  33.             LogUtils.d(TAG, "path: $path")
  34.             val uri = Uri.parse(path)
  35.             LogUtils.d(TAG, "uri: $uri")
  36.             imageUris.add(uri)
  37.         }
  38.         LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")
  39.         return imageUris
  40.     }
  41.     /**
  42.      * H5针对从文件钟选中的图片做处理
  43.      */
  44.     fun takeActivityResult(
  45.         requestCode: Int,
  46.         intent: Intent?,
  47.         filePathCallback: ValueCallback<Array<Uri>>?,
  48.     ) {
  49.         if (requestCode == REQUEST_PICTURE_SELECTOR) {
  50.             val selectList = PictureSelector.obtainMultipleResult(intent)
  51.             LogUtils.d(TAG, "getPicture selectList: $selectList")
  52.             // 将照片路径转换成 Uri 列表
  53.             val imageUris: MutableList<Uri> = ArrayList()
  54.             if (selectList.isEmpty()) {
  55.                 LogUtils.d(TAG, "getPicture selectList isEmpty")
  56.                 filePathCallback?.onReceiveValue(null)
  57.                 return
  58.             }
  59.             for (imagePath in selectList) {
  60.                 var path = imagePath.path
  61.                 LogUtils.d(TAG, "path: $path")
  62.                 val uri = Uri.parse(path)
  63.                 LogUtils.d(TAG, "uri: $uri")
  64.                 imageUris.add(uri)
  65.             }
  66.             LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")
  67.             filePathCallback?.onReceiveValue(imageUris.toTypedArray())
  68.         }
  69.     }
  70. }
复制代码
此中使用第三方组件库-实现类似朋侪圈获取照片的样式,需要引入一下依赖
  1.     //照片获取类微信朋友圈
  2.     implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
复制代码
照相后获取图片需要文件权限,界面跳转直接拿文件不需要权限,且第三方插件库里自带了静态权限以及权限哀求
3.我完备的代码——类似朋侪圈获取界面逻辑
  1. class VisitorFragment : Fragment() {    private lateinit var binding: FragmentVisitorBinding    lateinit var mActivity: Activity    private lateinit var mRoot: View    companion object {        const val TAG = "VisitorFragment"    }    private var mWebViewUrl: String = "file:///android_asset/vue_android_demo.html"    var mAppName = MainApplication.instance.configuration.BASE_APP_LOGIN_IDENTITY    var mSystemName = WebViewConstant.DEFAULT_SYSTEM_NAME    private var mVisitorAndroidJs: VisitorAndroidJs = VisitorAndroidJs(this)    private var mWebView: WebView? = null    var mAndroidId: String = WebViewConstant.DEFAULT_DEVICE_SIGN    var mApiKey: String = WebViewConstant.DEFAULT_API_KEY    //回传H5时使用的对象    private var mUploadCallback: ValueCallback<Array<Uri>>? = null    //照相通报的路径uri    private var mImageUri: Uri? = null    /**     * onCreate方法是Activity生命周期的第一个回调方法     * ,当Activity被创建时被调用。     * 在这个方法中,你可以举行一些初始化的操作,比如设置布局、绑定控件、初始化数据等。     *     * @param savedInstanceState If the fragment is being re-created from     * a previous saved state, this is the state.     */    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        binding = FragmentVisitorBinding.inflate(layoutInflater)        mRoot = binding.root        //记录        val viewModel = ViewModelProvider(requireActivity())[DashboardViewModel::class.java]        viewModel.setFragment(this)        LogUtils.d(TAG, "onCreate")    }    /**     * onCreateView方法是Fragment生命周期的回调方法,     * 当Fragment创建并绘制其用户界面时被调用。     * 在这个方法中,你可以通过返回一个View对象来定义Fragment的用户界面。     * 它常用于加载布局文件、查找和初始化控件等操作。     *     * @return     */    override fun onCreateView(        inflater: LayoutInflater,        container: ViewGroup?,        savedInstanceState: Bundle?    ): View {        LogUtils.d(TAG, "onCreateView")        initView(mRoot, layoutInflater, null)        return mRoot    }    fun initView(parent: View?, inflater: LayoutInflater?, container: ViewGroup?) {        //设置当前fragment        mAndroidId = DeviceUtils.getUniqueId(mActivity)        initWebView()        mWebView?.loadUrl(mWebViewUrl)        //弹出展示链接        showToast("${getString(R.string.current_develop_environment)}$mWebViewUrl")        initData()    }    override fun onAttach(context: Context) {        super.onAttach(context)        mActivity = context as Activity    }    /**     * @param msg 内容     */    fun showToast(msg: String?) {        val activity: Activity? = activity        activity?.runOnUiThread {            Toast.makeText(                activity,                msg,                Toast.LENGTH_SHORT            ).show()        }    }    private fun initData() {    }    override fun onResume() {        super.onResume()        LogUtils.d(TAG, "onResume")    }    @SuppressLint("SetJavaScriptEnabled")    private fun initWebView() {        LogUtils.d(TAG, "initWebView")        mWebView = binding.mainWebView        mWebView?.requestFocus()        mWebView?.isHorizontalScrollBarEnabled = false        mWebView?.isVerticalScrollBarEnabled = false        val setting = mWebView?.settings        setting?.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;        setting?.javaScriptEnabled = true        //用于开启或禁用其 DOM(文档对象模子)存储功能,浏览器缓存这些数据        setting?.domStorageEnabled = true        //允许访问文件,默认允许        setting?.allowFileAccess = true        //设置不,会引起webView重新不急,默认NARROW_COLUMNS        setting?.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS        //主动缩放        setting?.setSupportZoom(true)        setting?.builtInZoomControls = true        //自适应屏幕        setting?.useWideViewPort = true        setting?.loadWithOverviewMode = true        //支持多窗口        setting?.setSupportMultipleWindows(true)        setting?.setAppCacheEnabled(true)        setting?.domStorageEnabled = true        //定位        setting?.setGeolocationEnabled(true)        //优先使用缓存数据,在缓存数据不存在的情况下才去获取网络数据        setting?.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK        setting?.savePassword = false        //设置js接口        mVisitorAndroidJs.let { mWebView?.addJavascriptInterface(it, "android") }        //页面不跳转浏览器        mWebView?.webViewClient = object : WebViewClient() {            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {                LogUtils.d(TAG, "url: $url")                view.loadUrl(url)                return true            }            override fun shouldInterceptRequest(                view: WebView,                request: WebResourceRequest            ): WebResourceResponse? {                var uri = request.url                var path = uri.path                LogUtils.d(TAG, "uri: $uri, path: $path")                return super.shouldInterceptRequest(view, request)            }        }        //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
  2.         mWebView?.webChromeClient = object : WebChromeClient() {
  3.             //API >=21(android 5.0.1)回调此方法
  4.             override fun onShowFileChooser(
  5.                 webView: WebView?,
  6.                 filePathCallback: ValueCallback<Array<Uri>>?,
  7.                 fileChooserParams: FileChooserParams?
  8.             ): Boolean {
  9.                 mUploadCallback = filePathCallback
  10.                 //使用拍照或者打开文件
  11. //                mImageUri = ChoosePhotoFile.takePhoto(mActivity)
  12.                 //模拟微信朋友圈获取照片模式
  13.                 LogUtils.d(TAG,"onShowFileChooser")
  14.                 PictureSelectorUtils.startPictureSelector(mActivity)
  15.                 //设置false会IllegalStateException: Duplicate showFileChooser result
  16.                 return true
  17.             }
  18.         }
  19.     }    fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {        LogUtils.d(            TAG,            "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"        )        // 扫描二维码/条码回传        if (requestCode == ScanCodeUtils.REQUEST_CODE_SCAN && resultCode == Activity.RESULT_OK) {            LogUtils.d(TAG, "onActivityResultResponse ${intent?.extras}")            if (intent == null) {                //弹出展示链接                showToast("扫描效果为空")                return            }            //通报给js,格式是"scanCodeResult('${data.extras}')",此中单引号很紧张,大概导致js script error            //codedContent是组件中定义的参数名            setEvaluateJavascript("scanCodeResult('${intent.getStringExtra("codedContent")}')")//        } else if (requestCode == ChoosePhotoFile.REQUEST_CODE) {//            //照相,界面跳回后,效果文件的使用//            ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)        } else if (requestCode == PictureSelectorUtils.REQUEST_PICTURE_SELECTOR) {            LogUtils.d(TAG, "onActivityResultResponse REQUEST_PICTURE_SELECTOR")            PictureSelectorUtils.takeActivityResult(requestCode, intent, mUploadCallback)        }    }    @Override    override fun onDestroy() {        //防止更新dialog内存走漏        super.onDestroy()    }    /**     * 登录乐成后跳转     */    open fun loginSuccessJump() {    }    /**     * 给网页传值     * 通报给js,格式是"scanCodeResult('${data.extras}')"     * 此中单引号很紧张,大概导致js script error     */    private fun setEvaluateJavascript(jspMethodAndValue: String) {        LogUtils.d(            TAG,            "setEvaluateJavascript mWebView ${mWebView}, jspMethodAndValue $jspMethodAndValue"        )        mWebView?.evaluateJavascript(jspMethodAndValue, ValueCallback<String>() {            LogUtils.d(TAG, "给网页传值为: $jspMethodAndValue")        })    }}
复制代码
三、总结一下



  • H5调用公共获取图片文件方法,
  • 在android手机端,H5主要依赖Webview,
  • 这边在webview声明并重写该方法onShowFileChooser
  • 使用工具类打开相机或相册,可以两种方式安卓原生方式或者使用第三方组件库方式
  • 选中图片,返回uri列表给H5
  • H5收到uri照片列表,而且使用前端方式展示
创造价值,乐哉分享!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

铁佛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表