ScrollView嵌套WebView+原生控件的实现,可参考我的最新文章:ScrollView嵌套WebView与原生控件组合的一些问题


最近项目有一个需求,大概是上面为H5页面,下面是原生的List(用RecyclerView实现),拼接在一起。很自然想到用ScrollView来嵌套吧,虽然官方不建议,但也没办法咯!
谷歌的NestedScollView还是很好用的,解决了很多滑动冲突,尤其是配合ListView、RecyclerView这类控件时。

基本操作

布局大概是这样(省略其余无关内容):

<android.support.v4.widget.NestedScrollView
    android:id="@+id/test_scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <WebView
            android:id="@+id/test_web_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <include layout="@layout/layout_list" />

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>

然后按照常规操作初始化WebView布局,各种settings各种client重写方法,destroy时回收资源等等,这里就不赘述了(因为最后发现巨坑和这些初始化设置都无关)。
推荐一篇:史上最全WebView使用,附送Html5Activity一份

进坑

标题里说的坑是什么呢?
case1: 项目业务要求,在WebView所在的Activity启动之后,再通过网络请求去获取Web的url链接,请求成功回调才开始loadUrl。这个操作非常关键,因为中间有不确定的时差(测试的时候可以通过起一个延时线程来模拟)。
case2: 我们平时使用WebView,一般都是在初始化操作完成后立马loadUrl,而我们的WebView初始化往往都是在onCreate中进行的。
上述两个case是有区别的,其差异也是这个坑的起源。
坑来了:
对于case1,当我在网络请求的回调中加载url时,发现图文并茂的H5页面只剩下了铺满宽度的图片,文字内容完全没有了,原本很长的文章,其高度变成了屏幕的高度,不过下面的原生控件还是正常加载了的,ScrollView依然可以滑动。
WebView内的效果就像那种响应式的布局被宽高严重挤压一样,或者说是网页样式没有加载完全,但我JS支持也开启了的。
猜测原因,尝试出坑:
1、第一就想到了ScrollView嵌套的问题,因为WebView也是可以滑动的,这俩玩意在一起肯定会导致高度之类的坑,然后我把WebView移出ScrollView,果然就不会有问题了,但这并不能满足业务需求,只是缩小了问题分析范围。
2、然后谷歌百度了大量ScrollView嵌套WebView的坑,所有方法试了个遍,这篇文章基本上总结了所有相关解决办法(ScrollView嵌套WebView卡顿,进度条无限),什么JS注入,什么重新set布局参数,包括该文博主最后把WebView改为wrap_content解决问题,对我来说,全都没用。好气啊!
3、于是,我开始瞎搞,把网络请求强行延时3秒再回调,发现竟然没问题了,图文网页可以正常加载了。WTF?这到底是怎么回事?接着我又把测试用的url直接hard-coding到初始化后立即加载(相当于上述case2),也没有问题。
坑外分析:
这就怪了,也即是说loadUrl方法立即调用没问题,很久之后再调用也没问题,偏偏就网络请求并回调那几十毫秒~几百毫秒的区间中出问题。
联系刚才的高度问题,和方法调用的时机问题,想了下,可能跟ScrollView和WebView绘制测量布局的时刻有关,当我们在onCreate中loadUrl时,这时候用户还不可见呢,UI也没绘制,但网页的总高度已经拿到了,所以当ScrollView开始绘制时,已经知道了子控件WebView的高度,那么最终也就没有问题。
那为何很久之后再loadUrl也没问题呢?猜测是此时所有控件都已经绘制完了,但网页还没开始加载,所以高度是0,当再次加载时,触发重新测量,内容正常填充到WebView当中。
问题出在,控件在绘制过程中,由于网络请求回调是异步的,此刻加载网页,导致高度计算错误。
其实说了这么多,基本上都是我个人结合现象的猜测,具体没有实证,还望各位大佬指正原因。

妥协的解决

最终还是得解决问题,经过上述的分析,在满足业务需要的前提下,我的处理方式还是在初始化WebView结束后立即loadUrl,但这时候没有图文的真实url咋办呢?我们可以load一个空页面,比如"about:blank"之类的,或者让前端同学写一个简单的内容空白的html,这样即便加载实际的url时高度和空白页面高度不一样,也不会出现异常。

后话

其实后来我又想了下,现在这种混合实现方式的App这么多,为何它们没有任何问题?于是我去扒了下头条那些信息流的文章url,换到我的WebView里加载,竟然完美显示,不管我loadUrl的时刻是何时。这就很尴尬了,看来这个和网页本身也有关系的,仔细对比有问题的网页源代码,也没看出啥端倪,毕竟对前端不是特别熟悉,但总之这也是掉入巨坑的因素之一吧。
不过在我们不知道网页来源优质与否、是否可天然适应这种嵌套布局时,还是自己从原生层面规避比较好。