Android 点击WebView链接中的图片同比例放大,长按保存本地

龙旋

共 7175字,需浏览 15分钟

 · 2022-11-17

前言

现在纯原生应用已经越来越少,很多应用都是采用混合式开发 WebView 和 Javascript 进行交互,说到webView加载h5链接,很多时候都是h5写好了网页内容,android端只负责加载就完事了。

最近在研究webView加载h5链接时,看到一张很好看的gif图 但是没有办法下载呀,因为内容是h5的 android无权限操作 苦于烦恼 怎么办呢?

效果

代码实现

先说下思路:有2种方式可以实现,这里都会介绍到,第一种是拦截http的url的请求通过判断endsWith来取图片地址,但是这张方式有一种弊端,就是如果url不是以图片格式结尾,那么这个方法就没法使用比如:

https://inews.gtimg.com/newsapp_bt/0/14526974555/1000这张地址


URL拦截实现

 webView?.webViewClient = object : WebViewClient() {            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {                if (url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".png")                    || url.endsWith(".JPG") || url.endsWith(".JPEG") || url.endsWith(".PNG")                ) {                    openImageActivity(url)                    return true                }                return super.shouldOverrideUrlLoading(view, url)            }        }

这里的openImageActivity

   private fun openImageActivity(url: String) {        val bundle = Bundle()        bundle.putString("imgUrl", url)        toStartActivity(ShowWebImageActivity::class.java, bundle)    }


与JS通信实现

我是用这种方法实现的,也比较简单,没有弊端,无论你是图片格式结尾还是hmtl都可以使用glide直接加载

webView配置

        webView = mBind.webView        // 设置允许JS弹窗        webView?.settings?.javaScriptCanOpenWindowsAutomatically = true        webView?.settings?.defaultTextEncodingName = "UTF-8"//防止乱码        webView?.settings?.javaScriptEnabled = true        webView?.settings?.useWideViewPort = true        webView?.settings?.loadWithOverviewMode = true        webView?.settings?.setSupportMultipleWindows(true)        webView?.scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY //设置滚动条样式        // 最小缩放等级        webView?.setInitialScale(100)
//通过屏幕密度调整分辨率 val screenDensity = resources.displayMetrics.densityDpi var zoomDensity = ZoomDensity.MEDIUM when (screenDensity) { DisplayMetrics.DENSITY_LOW -> zoomDensity = ZoomDensity.CLOSE DisplayMetrics.DENSITY_MEDIUM -> zoomDensity = ZoomDensity.MEDIUM DisplayMetrics.DENSITY_HIGH -> zoomDensity = ZoomDensity.FAR } webView?.settings?.textSize = WebSettings.TextSize.LARGER //设置字体号150% webView?.settings?.defaultZoom = zoomDensity
//h5调用android webView?.addJavascriptInterface(JavaScriptInterface(this), "imagelistner")
if (vurLink != null) { webView?.loadUrl(vurLink) }


主要代码是这句 webview与js通信:

webView?.addJavascriptInterface(JavaScriptInterface(this), "imagelistner")


通过webView的onPageFinished方法 我们对url作监听

 override fun onPageFinished(view: WebView?, url: String?) {                super.onPageFinished(view, url)                //这段js函数的功能就是注册监听,遍历所有的img标签,并添加onClick函数,                // 函数的功能是在图片点击的时候调用本地java接口并传递url过去                webView?.loadUrl(                    "javascript:(function(){"                            + "var objs = document.getElementsByTagName(\"img\"); "                            + "for(var i=0;i<objs.length;i++)  " + "{"                            + "    objs[i].onclick=function()  " + "    {  "                            + "        window.imagelistner.openImageUrl(this.src);  "                            + "    }  " + "}" + "})()"                );
}
上述这段代码 我们需要注意window.imagelistner.openImageUrl(this.src)这句代码的imagelistner与openImageUrlwebView?.addJavascriptInterface(JavaScriptInterface(this), "imagelistner")定义的方法名,而 openImage就是我们自定义的 JavaScriptInterface 中的 openImage 方法


接下来我们定义JS接口

     // js通信接口    class JavaScriptInterface internal constructor(context: Context) {        private var mContext: Context = context
//https://inews.gtimg.com/newsapp_bt/0/14526974536/1000 @android.webkit.JavascriptInterface fun openImageUrl(img: String?) { img.logE("当前图片地址:") val intent = Intent(mContext, ShowWebImageActivity::class.java) intent.putExtra("img", img) ContextCompat.startActivity(mContext, intent, null) } }
这里的 JavascriptInterface 就是和 webView?.addJavascriptInterface(JavaScriptInterface(this), "imagelistner"), 中的 new JavascriptInterface(this) 对应的,这样就实现通信了


展示及下载

通过上面的通信,我们从js那边拿到了图片的url 这样对于我们来说就是小菜一碟了,直接加载就完事了  具体看代码实现,这里要注意,我们需要把Activity改成dialog的形式,不然一张图就占全屏,属实浪费,也不好看。

你还记得android初学的问题 如何把activity改成dialog吗?还要去掉activity的title,不然有个应用名的标题,看起来很奇怪。

override fun initView(savedInstanceState: Bundle?) {        title = null  //去标题        val url = intent.getStringExtra("img") //接受url        if (url != null) {            Glide.with(this)                .load(url)                .transition(DrawableTransitionOptions.withCrossFade(500))                .into(mBind.imageShow)
}
//长按下载图片 mBind.imageShow.setOnLongClickListener { try { if (url != null) { mViewModel.uploadPhotoUrl = url mViewModel.downLoad({ //下载中 mBind.tvDownNumber.visibility = View.VISIBLE mBind.tvDownNumber.text = "下载进度:${it.progress}%" }, { //下载完成 mBind.tvDownNumber.visibility = View.GONE showDialogMessage("下载成功,路径为:${it}") }, { //下载失败 mBind.tvDownNumber.visibility = View.GONE showDialogMessage(it.msg) }) } } catch (ex: Exception) { ToastUtils.show("save pic error:$ex") } true        }

关于图片下载 我这里适配了android10及以上版本 代码参考一下

 /**     * 下载     * @param downLoadData Function1<ProgressT<String>, Unit>     * @param downLoadSuccess Function1<String, Unit>     * @param downLoadError Function1<Throwable, Unit>     */    fun downLoad(downLoadData: (Progress) -> Unit = {}, downLoadSuccess: (String) -> Unit, downLoadError: (Throwable) -> Unit = {}) {        viewModelScope.launch {            if (checkedAndroid_Q()) {                //android 10 以上                val factory = Android10DownloadFactory(appContext, "${System.currentTimeMillis()}")                RxHttp.get(uploadPhotoUrl)                    .toFlow(factory) {                        downLoadData.invoke(it)                    }.catch {                        //异常回调                        downLoadError(it)                    }.collect {                        //成功回调                        downLoadSuccess.invoke(UriUtils.getFileAbsolutePath(appContext,it)?:"")                    }            } else {                //android 10以下                val localPath = appContext.externalCacheDir!!.absolutePath + "/${System.currentTimeMillis()}"                RxHttp.get(uploadPhotoUrl)                    .toFlow(localPath) {                        downLoadData.invoke(it)                    }.catch {                        //异常回调                        downLoadError(it)                    }.collect {                        //成功回调                        downLoadSuccess.invoke(it)                    }            }        }    }


这里提供思路,具体实现各位可以尝试一下,到这里就结束啦。


浏览 50
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报