从集成Scratch来踩webview的坑

2021-02-11  本文已影响0人  苏联老斯基

大家好,我是一个默默无闻的小Android搬运工。
最近,领导问我,小伙子忙不忙啊,公司要做幼儿编程,你准备一下吧。
我说怎么回事,领导给我发了个编程猫APP,就按照这个做。
我一看,哦,以前没研究过啊,这是什么鬼,然后前端小伙伴给了我一个本地HTML代码,我一看,so easy啊,这不就是webview加载本地HTML么,5分钟就能结束。
说做就做,小代码咔咔一写

webview.loadurl("file:///android_asset/html/index.html")
很快啊,啪,项目运行起来了,点一点,诶,能点,看来可以了,再点一点,诶,这个保存怎么没反应,这个打开文件怎么没反应
这就让我这个CV工程师脸没处放了,那必须得解决了,一步一步来

1.保存文件->下载blob类型文件

[img]http://chuantu.xyz/t6/741/1613038816x2073530471.png
-配置webview

      webSettings.defaultTextEncodingName = "utf-8"    //设置编码
      webSettings.cacheMode = WebSettings.LOAD_DEFAULT  //设置缓存策略
      webSettings.javaScriptEnabled = true  //启用js(重要)
      webSettings.loadWithOverviewMode = true  //是否使用概览模式
      webSettings.useWideViewPort = true  //设置屏幕自适应

-添加下载回调

web.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
            //下载代码
        }

此回调会返回下载url
-注入js代码(重头戏,)
首先js代码

  request.open('GET', '" + url + "', true);
  request.setRequestHeader('Content-type', 'text/plain');
  request.responseType = 'blob';
  request.onload = function (e) {
       if (this.status === 200) {
               var blobFile = this.response;
               var reader = new FileReader();
               reader.readAsDataURL(blobFile);
               reader.onloadend = function() {
               var base64data = reader.result;
               window.java.down(base64data);
               }
           }
  };
  request.send();

只需要把url传进去就好了
完整代码

val blob = "  var request = new XMLHttpRequest();" +
                    "        request.open('GET', '" + url + "', true);" +
                    "        request.setRequestHeader('Content-type', 'text/plain');" +
                    "        request.responseType = 'blob';" +
                    "        request.onload = function (e) {" +
                    "            if (this.status === 200) {" +
                    "                var blobFile = this.response;" +
                    "                var reader = new FileReader();" +
                    "                reader.readAsDataURL(blobFile);" +
                    "                reader.onloadend = function() {" +
                    "                var base64data = reader.result;" +
                    "                window.java.down(base64data);" +
                    "                }" +
                    "             }" +
                    "        };" +
                    "        request.send();"
val js = "javascript:$blob"
web.evaluateJavascript(js,null)    //注入js代码并执行
web.addJavascriptInterface(object : Any() {
            @JavascriptInterface
            fun down(base64: String?) {
                  //得到下载的base64字符串,需自行转换
            }
        }, "java")

2.上传文件,打开文件管理器

webview配置如上
-重写WebChromeClient

class ZpWebChromeClient : WebChromeClient() {
    private var mOpenFileChooserCallBack: OpenFileChooserCallBack? = null

    //For Android 3.0 - 4.0
    // For Android < 3.0
    @JvmOverloads
    fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String = "") {
        if (mOpenFileChooserCallBack != null) {
            mOpenFileChooserCallBack!!.openFileChooserCallBack(uploadMsg, acceptType)
        }
    }

    // For Android 4.0 - 5.0
    fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
        openFileChooser(uploadMsg, acceptType)
    }

    override fun onConsoleMessage(cm: ConsoleMessage?): Boolean {
        println(("JavaScript log, " + cm!!.sourceId() + ":" + cm.lineNumber() + ", " + cm.message()))
        return true
    }

    // For Android > 5.0
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean {
        if (mOpenFileChooserCallBack != null) {
            mOpenFileChooserCallBack!!.showFileChooserCallBack(filePathCallback, fileChooserParams)
        }
        return true
    }

    fun setOpenFileChooserCallBack(callBack: OpenFileChooserCallBack?) {
        mOpenFileChooserCallBack = callBack
    }

    override fun onProgressChanged(view: WebView, newProgress: Int) {
        super.onProgressChanged(view, newProgress)
    }

    interface OpenFileChooserCallBack {
        fun openFileChooserCallBack(uploadMsg: ValueCallback<Uri>, acceptType: String)    //打开文件选择回调
        fun showFileChooserCallBack(filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams)    //选择文件回调
    }

Activity实现ZpWebChromeClient.OpenFileChooserCallBack并设置webview.setwebChromeClient,然后自行处理文件选择就可,别忘了在onActivityResult获取回调

调用手机摄像头

此处就比较简单了,js配置依旧如上
在自定义WebChromeClient中添加onPermissionRequest,然后activity中会调用增加拦截权限代码

/* access modifiers changed from: private */
    fun requestCameraPermissions(permissionRequest2: PermissionRequest) {
        mViewModel.permissionRequest = permissionRequest2
        val arrayList = ArrayList<Any>()
        for (str in permissionRequest2.resources) {
            if (str == "android.webkit.resource.VIDEO_CAPTURE" && ContextCompat.checkSelfPermission(
                            this,
                            mViewModel.ANDROID_PERMISSION_CAMERA
                    ) != 0
            ) {
                arrayList.add(mViewModel.ANDROID_PERMISSION_CAMERA)
            }
            if (str == "android.webkit.resource.AUDIO_CAPTURE" && ContextCompat.checkSelfPermission(
                            this,
                            mViewModel.ANDROID_PERMISSION_RECORD_AUDIO
                    ) != 0
            ) {
                arrayList.add(mViewModel.ANDROID_PERMISSION_RECORD_AUDIO)
            }
        }
        if (!arrayList.isEmpty()) {
            ActivityCompat.requestPermissions(
                    this,
                    (arrayList.toArray(arrayOfNulls<String>(arrayList.size)) as Array<String?>),
                    2
            )
        } else {
            grantPermission()
        }
    }

    private fun grantPermission() {
        val permissionRequest2: PermissionRequest =
                mViewModel.permissionRequest as PermissionRequest
        permissionRequest2.grant(permissionRequest2.resources)
        mViewModel.permissionRequest = null
    }

此段代码即可调用摄像头

手动向webview添加header信息

虽然现在webview支持自动添加头部信息,但是,拦不住特殊需求啊,产品非要略过登录直接到下一步,没得办法只能手动添加header了。
-依旧是启用js
-注入js
在WebViewClient的onPageFinished方法中添加js代码

val key = "pro__Access-Token"
val js = "window.localStorage.setItem('$key','${
      mViewModel.local(let {
             PreferenceUtils.getString(
                   this@WriteActivity,
                   "token"
             )
        } as String)
       }');"
view.evaluateJavascript(js) { value: String ->
println(
         "localStorage:$value"
         )
}

需要注意的是,代码中的key值和value值需要和前端沟通,规定好要什么给什么

到这里遇到的坑就暂时填上了,等到遇到了再研究

源码地址,由于涉及公司项目所以不增加html页面

上一篇下一篇

猜你喜欢

热点阅读