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

04-10 1444阅读

需求目的: 手机机通过webView展示H5网页,在特殊场景下,需要使用相机拍照或者从相册获取照片,上传后台。

完整流程效果: 如下图

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

一、H5界面样例代码

使用html文件格式,文件直接打开就可以展示布局;一会在andriod webview中直接加载



    
    
    Document

alllalalallalal 默认会被覆盖

{{message}}

{{counter}}

+1 -1

{{ title }}

android选中照片H5展示

Vue.createApp({ template: '#why', data: function () { return { message: "功能开发中,敬请期待!", counter: 100, pictureSelectorContent: "相机选择结果:", imageSources: [] // 存储图片的数据URL } }, // 在你的 Vue 组件中处理 Webview 传递的数据 mounted() { // 设置全局函数,用于接收 WebView 调用 // window.pictureSelectorResult = this.pictureSelectorResult; }, methods: { increment() { this.counter++; console.log("点击了+1"); }, decrement() { this.counter--; consloe.log("点击了-1"); }, startPictureSelector() { window.android.startPictureSelector(); }, previewFiles() { const files = this.$refs.imgFile.files; this.imageSources = []; for (let i = 0; i { this.imageSources.push(e.target.result); }; reader.readAsDataURL(file); } }, }, }).mount("#app") h1 { font-size: 80px; font-weight: bold; margin-bottom: 20px; } h2 { font-size: 20px; font-weight: bold; color: #C8EFD4; } h3 { font-size: 10px; font-weight: bold; color: #C8EFD4; } button1 { font-size: 60px; padding: 10px 20px; background-color: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; margin-bottom: 20px; margin-top: 20px; text-align: center; /* 将文字水平居中显示 */ display: flex; /* 将按钮设置为flex容器 */ align-items: center; /* 将文字在垂直方向上居中显示 */ }

上述代码是前端代码,使用vue3框架展示一个基础 加减demo界面(不重要的冗余),以及 一个打开文件的按钮以及展示图片

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

其中HTML5提供了元素来实现选取文件的功能,当在WebView表现为调用onShowFileChooser后,回调图片uri列表一一获取并展示

二、Android打开相机以及相册的两种方式

方式一:android 原生方式

实际效果和流程示图

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

1.android界面逻辑代码

这边使用的是kotlin语言,使用的fragment界面展示,使用binding加载了布局,以及声明了webview组件,在webview上导入html链接,使本地H5界面展示

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? = 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,把网页回传文件
        mWebView?.webChromeClient = object : WebChromeClient() {
            //API >=21(android 5.0.1)回调此方法
            override fun onShowFileChooser(
                webView: WebView?,
                filePathCallback: ValueCallback?,
                fileChooserParams: FileChooserParams?
            ): Boolean {
                mUploadCallback = filePathCallback
                //使用拍照或者打开文件
                mImageUri = ChoosePhotoFile.takePhoto(mActivity)
                //设置false会IllegalStateException: Duplicate showFileChooser result
                return true
            }
        }
    }
    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)
        }
    }
    @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() {
            LogUtils.d(TAG, "给网页传值为: $jspMethodAndValue")
        })
    }
}

android layout布局


    

2.获取照片的主要思路是两个方法:

  • webView官方打开文件选取方法onShowFileChooser,把网页回传文件
            //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
            mWebView?.webChromeClient = object : WebChromeClient() {
                //API >=21(android 5.0.1)回调此方法
                override fun onShowFileChooser(
                    webView: WebView?,
                    filePathCallback: ValueCallback?,
                    fileChooserParams: FileChooserParams?
                ): Boolean {
                    mUploadCallback = filePathCallback
                    //使用拍照或者打开文件
                    mImageUri = ChoosePhotoFile.takePhoto(mActivity)
                    //设置false会IllegalStateException: Duplicate showFileChooser result
                    return true
                }
            }
    
    • 拍照或者相册选中后界面跳回后,使用ValueCallback回传
          fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {
              LogUtils.d(
                  TAG,
                  "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"
              )
      		 if (requestCode == ChoosePhotoFile.REQUEST_CODE) {
                  //拍照或者相册选中后界面跳回后,结果文件的使用
                  ChoosePhotoFile.takeActivityResult(requestCode, intent, mUploadCallback, mImageUri)
              }
          }
      
      • 以上onActivityResultResponse方法需要在actvity onActivityResult方法中使用

        (因为我这里是activity嵌套fragment的,如果直接在activity使用webview就不需我这太麻烦)

            override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
                super.onActivityResult(requestCode, resultCode, data)
                LogUtils.d(
                    TAG,
                    "onActivityResult requestCode11 $requestCode, resultCode:$resultCode"
                )
                //设置当前fragment
                val fragment = mDashboardViewModel.fragment.value
                Log.d(TAG, "fragment: $fragment")
                Log.d(TAG, "fragment.isResumed: ${fragment?.isResumed}")
                //界面返回时VisitorFragment还没有Resumed
                if (fragment is VisitorFragment) {
                    val visitorFragment: VisitorFragment = fragment
                    visitorFragment.onActivityResultResponse(requestCode, resultCode, data)
                }
            }
        

        3.打开相机和相册的工具类

        object ChoosePhotoFile {
            private const val TAG = "ChoosePhotoFile"
            const val REQUEST_CODE: Int = 12345
            fun takePhoto(activity: Activity): Uri {
                //相机可以访问的公共位置才能存储,获取时需要读取文件权限
                val file: String =
                    Environment.getExternalStorageDirectory()
                        .toString() + File.separator + Environment.DIRECTORY_PICTURES + File.separator
                val fileName = "Image_${SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())}.jpg"
                val realFile = File(file, fileName)
                val imageUri = Uri.fromFile(realFile)
                LogUtils.d(TAG, "realFile:$realFile, imageUri: $imageUri")
                // 拍照后获取图片需要文件权限,界面跳转直接拿文件不需要权限
                //检查申请读文件权限
                CheckPermissionUtils.requestPermissions(
                    activity,
                    arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
                )
                //调起相机,拍一张照片
                val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
                //调起相册,取一张照片
                val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
                //选择方式,拍照或者相册
                val chooserIntent = Intent.createChooser(photoIntent, "Image Chooser")
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(captureIntent))
                activity.startActivityForResult(chooserIntent, REQUEST_CODE)
                return imageUri
            }
            /**
             * H5针对从拍照或者相册中选中的图片做处理
             * @param imageUri 拍照返回的数据,
             */
            fun takeActivityResult(
                requestCode: Int,
                intent: Intent?,
                filePathCallback: ValueCallback?,
                imageUri: Uri?
            ) {
                if (requestCode == REQUEST_CODE) {
                    //从相册获取,返回的intent
                    if (intent != null && intent.data != null) {
                        var uri: Uri = intent.data as Uri
                        LogUtils.d(TAG, "file uri: $uri")
                        filePathCallback?.onReceiveValue(arrayOf(uri))
                    } else {
                        //从拍照中获取图片,已经返回的imageUri
                        LogUtils.d(TAG, "take photo imageUri: $imageUri")
                        if (imageUri != null) {
                            filePathCallback?.onReceiveValue(arrayOf(imageUri))
                        } else {
                            filePathCallback?.onReceiveValue(null)
                        }
                    }
                }
            }
        }
        
        方式二:使用android 组件库是实现-朋友圈获取照片功能

        实际效果和流程示图

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

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

        • webView官方打开文件选取方法onShowFileChooser,把网页回传文件
                  //webView官方打开文件选取方法onShowFileChooser,把网页回传文件
                  mWebView?.webChromeClient = object : WebChromeClient() {
                      //API >=21(android 5.0.1)回调此方法
                      override fun onShowFileChooser(
                          webView: WebView?,
                          filePathCallback: ValueCallback?,
                          fileChooserParams: FileChooserParams?
                      ): Boolean {
                          mUploadCallback = filePathCallback
                          //使用拍照或者打开文件
          //                mImageUri = ChoosePhotoFile.takePhoto(mActivity)
                          //模拟微信朋友圈获取照片模式
                          LogUtils.d(TAG,"onShowFileChooser")
                          PictureSelectorUtils.startPictureSelector(mActivity)
                          //设置false会IllegalStateException: Duplicate showFileChooser result
                          return true
                      }
                  }
          
          • 拍照或者相册选中后界面跳回后,使用ValueCallback回传
                fun onActivityResultResponse(requestCode: Int, resultCode: Int, intent: Intent?) {
                    LogUtils.d(
                        TAG,
                        "onActivityResultResponse requestCode $requestCode, resultCode:$resultCode"
                    )
            		 if (requestCode == PictureSelectorUtils.REQUEST_PICTURE_SELECTOR) {
                        LogUtils.d(TAG, "onActivityResultResponse REQUEST_PICTURE_SELECTOR")
                        PictureSelectorUtils.takeActivityResult(requestCode, intent, mUploadCallback)
                    }
                }
            

            2.打开相机和相册的工具类

            object PictureSelectorUtils {
                const val REQUEST_PICTURE_SELECTOR = 10012
                const val TAG = "PictureSelectorUtils"
                fun startPictureSelector(activity: Activity) {
                    LogUtils.d(TAG, "startPictureSelector")
                    // 拍照后获取图片需要文件权限,界面跳转直接拿文件不需要权限
                    //检查申请读文件权限
            //        CheckPermissionUtils.requestPermissions(
            //            activity,
            //            arrayOf(
            //                Manifest.permission.CAMERA,
            //                Manifest.permission.READ_EXTERNAL_STORAGE,
            //                Manifest.permission.WRITE_EXTERNAL_STORAGE
            //            )
            //        )
                    //插件里自带了静态权限以及权限校验
                    PictureSelector.create(activity).openGallery(PictureMimeType.ofImage())
                        .imageEngine(GlideEngine) // Please refer to the Demo GlideEngine.java
                        .isWeChatStyle(true) // 是否开启微信图片选择风格
                        .selectionMode(PictureConfig.MULTIPLE).forResult(REQUEST_PICTURE_SELECTOR)
                }
                fun getPictures(data: Intent): MutableList {
                    val selectList = PictureSelector.obtainMultipleResult(data)
                    LogUtils.d(TAG, "getPicture selectList: $selectList")
                    // 将照片路径转换成 Uri 列表
                    val imageUris: MutableList = ArrayList()
                    if (selectList.isEmpty()) {
                        LogUtils.d(TAG, "getPicture selectList isEmpty")
                        return imageUris
                    }
                    for (imagePath in selectList) {
                        var path = imagePath.path
                        LogUtils.d(TAG, "path: $path")
                        val uri = Uri.parse(path)
                        LogUtils.d(TAG, "uri: $uri")
                        imageUris.add(uri)
                    }
                    LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")
                    return imageUris
                }
                /**
                 * H5针对从文件钟选中的图片做处理
                 */
                fun takeActivityResult(
                    requestCode: Int,
                    intent: Intent?,
                    filePathCallback: ValueCallback?,
                ) {
                    if (requestCode == REQUEST_PICTURE_SELECTOR) {
                        val selectList = PictureSelector.obtainMultipleResult(intent)
                        LogUtils.d(TAG, "getPicture selectList: $selectList")
                        // 将照片路径转换成 Uri 列表
                        val imageUris: MutableList = ArrayList()
                        if (selectList.isEmpty()) {
                            LogUtils.d(TAG, "getPicture selectList isEmpty")
                            filePathCallback?.onReceiveValue(null)
                            return
                        }
                        for (imagePath in selectList) {
                            var path = imagePath.path
                            LogUtils.d(TAG, "path: $path")
                            val uri = Uri.parse(path)
                            LogUtils.d(TAG, "uri: $uri")
                            imageUris.add(uri)
                        }
                        LogUtils.d(TAG, "imageUris: ${imageUris.toString()}")
                        filePathCallback?.onReceiveValue(imageUris.toTypedArray())
                    }
                }
            }
            

            其中使用第三方组件库-实现类似朋友圈获取照片的样式,需要引入一下依赖

                //照片获取类微信朋友圈
                implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
            

            拍照后获取图片需要文件权限,界面跳转直接拿文件不需要权限,且第三方插件库里自带了静态权限以及权限请求

            3.我完整的代码——类似朋友圈获取界面逻辑

            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? = 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,把网页回传文件
                    mWebView?.webChromeClient = object : WebChromeClient() {
                        //API >=21(android 5.0.1)回调此方法
                        override fun onShowFileChooser(
                            webView: WebView?,
                            filePathCallback: ValueCallback?,
                            fileChooserParams: FileChooserParams?
                        ): Boolean {
                            mUploadCallback = filePathCallback
                            //使用拍照或者打开文件
            //                mImageUri = ChoosePhotoFile.takePhoto(mActivity)
                            //模拟微信朋友圈获取照片模式
                            LogUtils.d(TAG,"onShowFileChooser")
                            PictureSelectorUtils.startPictureSelector(mActivity)
                            //设置false会IllegalStateException: Duplicate showFileChooser result
                            return true
                        }
                    }
                }
                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() {
                        LogUtils.d(TAG, "给网页传值为: $jspMethodAndValue")
                    })
                }
            }
            

            三、总结一下

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

              创造价值,乐哉分享!

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]