Android项目开发中针对webview避免不了混合开发模式,可Android上的webview又很不争气,几乎每个Android版本都有webvie的改动,在国内这种鱼龙混杂环境下出乎意料的问题又很多,这次就谈谈webview高度自适应的问题。
问题
在Android开发生涯中想必大家都遇到过,webview在有些时候展示不完整,在某些时候底部有一片空白,有些时候始终又滑不到底。。。导致这个问题的原因常见有以下三种:
- 网页高度不固定
- 嵌套布局导致webview的高度无法自适应
- 系统原因导致webview在某种特殊情况下不能兼容当前应用的屏幕分辨率
下边就分别说说这三种情况引起的原因以及解决方法。
针对网页高度不固定问题
这部分原因和H5编写方法有很大关系,例如Ajax异步处理结果过慢,webview的高度不能及时更新;也有可能H5编写时预留了一个资源展示区等待将来资源展示,结果资源没有加载出来。这个和H5开发人员说明一下情况,检查一下代码结构。
针对嵌套布局导致webview高度不固定问题
这个是Android开发最常见的事,很多时候是混合开发模式,不得已采用scrollview嵌套webview,但是我们知道scrollview中嵌套其他view常常使某些view的高度(match_parent)失效,当然你可以采用android:fillViewport="true"
强制让其撑满,但是似乎其内部的webview又不听话了,这个道理很明显,毕竟webview渲染的是网页,也即是我们能够准确固定webview高度这个问题就迎刃而解了。
针对webview兼容当前应用屏幕密度问题
首先我们要处理的是避免系统字体的缩放影响webview缩放,我们可以用webview.getSettings().setTextZoom(100)
来避免。其余的支持自适应即可,在应用屏幕密度设定之后webiview可自动匹配,当然也有一些奇葩设备(被应用商修改过),这部分我们可以忽略了。
针对高度不固定问题解决方案
我这里给出两种解决方案。
布局结构调整
按照官方建议webiview父布局高度采用match_parent
。
WebView根据网页的高度自动扩展自身高度:
我们按照步骤走:
第一步:本地定义Js交互接口,通常H5需要明确这个接口标识,Android端才能回调相关内容给H5。
mWebView.addJavascriptInterface(this, "App");
第二步:本地实现接口方法,H5通过上边的“APP”接口标识能调用本地该接口里面的方法。
/**
* js回调,重新计算webview的高度。
*/
@JavascriptInterface
public void resize(final float clientWidth, final float scrollHeight) {
if (mActivity == null || mWebView == null || clientWidth == 0) {
return;
}
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
float rate = mWebView.getWidth() / clientWidth;
Log.i("TAG", "webview---------clientWidth:" + clientWidth + "---------scrollHeight:" + scrollHeight + "---------rate:" + rate);
mWebView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
(int) (scrollHeight * rate)));
}
});
}
第三步:本地调用H5的js方法,告诉H5:“我,大Android,需要你H5现在的宽和高”,然后H5乖乖的把界面上宽高回调给本地,就是第二步的方法。但是!我今天说的这个是通用方式,是针对所有的H5的,不可能让每个H5都定义一个js方法供Android端调用,怎么办呢?好办,直接让H5回调本地方法:
mWebView.loadUrl("javascript:App.resize(document.documentElement.clientWidth, document.documentElement.scrollHeight)");
这里的“App”其实就是“this”,那么js回调时机是什么时候呢,我是希望这个页面加载结束的时候告诉我,所以重写WebViewClient里面的onPageFinished
方法。由于有些H5采用异步加载的方式,我不知道他到底什么时候完全加载出来,所以这里我分多次让H5帮我回调:
class DefaultWebViewClient extends WebViewClient {
//页面内跳转
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String s) {
webView.loadUrl(s);
return true;
}
@Override
public void onPageFinished(WebView webView, String s) {
super.onPageFinished(webView, s);
//执行网页高度获取,不断获取网页高度
mHandler.post(new Runnable() {
//执行最大次数
int times = 5;
@Override
public void run() {
if (times > 0) {
mWebView.loadUrl("javascript:App.resize(document.documentElement.clientWidth, document.documentElement.scrollHeight)");
mHandler.postDelayed(this, 1000);
times--;
}
}
});
}
}
总结
以上是我针对该类问题想到的一些处理方案,大家有更好的处理方式不妨也分享一下,集思广路,一同进步。