最近在写一个APP时,需要显示文章详情页,准备使用WebView和RecyclerView实现上面文章,下面评论。出现了WebView高度问题,WebView加载HTML格式数据,而非URL

  • 这里的WebView为自定义组件NestedScrollingWebView,已解决嵌套滑动问题。

问题1

如果WebView设置为wrap_content,可能由于网络和性能会出现文章加载没有评论快,导致评论会在WebView渲染数据时提前显示在上面的情况,很不美观。

点击查看问题

问题2

如果WebView设置为match_parent,当文章高度不足一屏时,下面空白太大,不美观。

点击查看问题

left

文章内容超过一屏幕时
文章内容超过一屏幕时

right

文章内容不足一屏幕时
文章内容不足一屏幕时

解决方案

效果

可以先看一下效果

点击查看效果

思路

利用JS获取高度,然后通知(loadUrl(js))WebView改变高度。关于JS获取高度,这里采用了一种我觉得很准确的方法

private fun getHtmlData(title:String, bodyHTML: String): String {
val head = ("<head>" +
"<meta name=\"viewport\" " +
"content=\"width=device-width, " +
"initial-scale=1.0, user-scalable=no\"> " +
"<style></style></head>")
return "<html>$head<body>" +
"<h2 class='title'>$title</h2>$bodyHTML<div class='bottom'></div>" +
"</body></html>"
}

在文章内容的最下面加一个div,通过document.querySelector('.bottom').offsetTop来用于确定高度

具体方法

1、先创建一个Mobile

private inner class Mobile {
@JavascriptInterface
fun onGetWebContentHeight(height: Int) {
contentWV.post {
val layoutParams = contentWV.layoutParams
layoutParams.height = Utils.getPixelByDp(this@JsSetHeightActivity, height)
contentWV.layoutParams = layoutParams
Log.i(TAG, "onGetWebContentHeight: height=$height")
}
}
}

2、在初始化WebView时,设置必要参数

private fun initView() {
contentWV = findViewById<NestedScrollingWebView>(R.id.content_wv)

// 开启js
val setting = contentWV.settings
setting.javaScriptEnabled = true

// 添加JS接口
val mobile = Mobile()
contentWV.addJavascriptInterface(mobile, "mobile")

// 在 onPageFinished时重新设置高度
val webClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
val js = "javascript:mobile.onGetWebContentHeight(document.querySelector('.bottom').offsetTop)"
view?.loadUrl(js)
}
}
contentWV.webViewClient = webClient
}

在页面加载完成之后,会重新设置高度

Demo下载

文章详情 Demo下载

参考

文中的WebView以及NestedScroll的用法参考:10分钟带你入门NestedScrolling机制 - SegmentFault 思否

其他实现方案:上面webview 下边评论 (applemei.com)