I\'m trying to get the height of a webview once it has been rendered. It always returns null, I\'ve tried getHeight
, getMeasuredHeight
, getCo
I chose this approach
const val heightWebViewJSScript = "(function() {var pageHeight = 0;function findHighestNode(nodesList) { for (var i = nodesList.length - 1; i >= 0; i--) {if (nodesList[i].scrollHeight && nodesList[i].clientHeight) {var elHeight = Math.max(nodesList[i].scrollHeight, nodesList[i].clientHeight);pageHeight = Math.max(elHeight, pageHeight);}if (nodesList[i].childNodes.length) findHighestNode(nodesList[i].childNodes);}}findHighestNode(document.documentElement.childNodes); return pageHeight;})()"
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
webView.evaluateJavascript(heightWebViewJSScript
) { height ->
val params = itemView.layoutParams
// params.height
}
}
}
You can use ViewTreeObserver
on that WebView
to get actual height after rendering its content.
here's the sample code.
ViewTreeObserver viewTreeObserver = mWebView.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
int height = mWebView.getMeasuredHeight();
if( height != 0 ){
Toast.makeText(getActivity(), "height:"+height,Toast.LENGTH_SHORT).show();
mWebView.getViewTreeObserver().removeOnPreDrawListener(this);
}
return false;
}
});
I found this solution to be 100% reliable.
Subclass your WebView and there is a need to invoke javascript after the content has been loaded.
// callback made this way in order to get reliable html height and to avoid race conditions
@SuppressLint("SetJavaScriptEnabled")
override fun onPageFinished(view: WebView?, url: String?) {
view?.let {
it.settings.javaScriptEnabled = true
it.addJavascriptInterface(WebAppInterface(it), "AndroidGetHeightFunction")
it.loadUrl("javascript:AndroidGetHeightFunction.resize(document.body.scrollHeight)")
}
}
We can then get proper height and disable javascript in callback (for security and consistency):
inner class WebAppInterface(private val webView: WebView) {
@JavascriptInterface
fun resize(height: Float) {
webView.post {
heightMeasuredListener?.invoke(formatContentHeight(webView, height.toInt()))
webView.settings.javaScriptEnabled = false
}
}
}
WebView must call post() as the code inside resize(...) is called in WebView thread!
After, make sure to scale your pixels to match density pixels!:
fun formatContentHeight(webView: WebView, height: Int): Int = Math.floor((height * webView.context.resources.displayMetrics.density).toDouble()).toInt()
ok, this is pretty later, but I think it nice solution,
just use if (webView.getContentHeight() > 0)
if ture
it means it finsihed, else, it not finished yet
Tried and tested the following:
mWebView = new WebView(this);
mWebView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View view, int l, int t, int r, int b, int ol, int ot, int or, int ob) {
Log.i("demo", String.format("%s: top=%d, bottom=%d, content height=%d",
Thread.currentThread().getStackTrace()[2].getMethodName(), t, b, mWebView.getContentHeight()
));
}
});
Logs:
12-20 16:08:33.969 1466-1466/yourPkg I/demo: onLayoutChange: top=0, bottom=0, content height=0
12-20 16:08:33.970 1466-1466/yourPkg I/demo: onLayoutChange: top=0, bottom=0, content height=0
12-20 16:08:34.061 1466-1466/yourPkg I/demo: onLayoutChange: top=0, bottom=1510, content height=0
12-20 16:08:34.091 1466-1466/yourPkg I/demo: onLayoutChange: top=0, bottom=1510, content height=2050
The last one is correct.
ViewTreeObserver
sometimes gave me the wrong height.