WebView高度自适应方案探究

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--;
                }
            }
        });
    }
}

总结

以上是我针对该类问题想到的一些处理方案,大家有更好的处理方式不妨也分享一下,集思广路,一同进步。

上一篇:【html】iframe 的高度自适应


下一篇:ehcache memcache redis 三大缓存男高音