前言
现在纯原生应用已经越来越少 很多应用都是采用
混合式开发 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
上述这段代码 我们需要注意
window.imagelistner.openImageUrl(this.src)
这句代码的imagelistner
与openImageUrl
webView?.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, Unit>
* @param downLoadSuccess Function1
* @param downLoadError Function1
*/
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)
}
}
}
}
总结
学习是没有止境的 只要一点点进步 就不会被社会淘汰
2022 继续努力 ~