Android NestedScrollView内嵌WebView回滑后内容无法滚动修复
问题根因
该滑动失效问题由三个配置错误共同导致:
- WebView设置了
android:isScrollContainer="false",直接禁用了自身的滚动响应能力,所有触摸事件默认全部上交父容器 - WebView错误配置了仅适用于CoordinatorLayout直接子View的
layout_behavior、layout_scrollFlags属性,干扰了嵌套滑动的事件分发逻辑 - NestedScrollView与下方RecyclerView平级放置,两个可滚动容器争抢事件焦点,滑动经过RecyclerView区域再返回WebView位置时,事件分发链路断裂,WebView无法接收到滚动事件。
修复方案
按以下步骤调整即可解决:
- 调整布局层级与属性
- 将原本和NestedScrollView平级的RecyclerView移入NestedScrollView内部,作为垂直布局的子项,同时给RecyclerView设置
android:nestedScrollingEnabled="false",禁用自身滚动,统一由外层NestedScrollView调度滚动事件 - 移除WebView上多余的
layout_behavior、layout_scrollFlags属性,这类属性仅对CoordinatorLayout的直接子View生效 - 将WebView的
android:isScrollContainer改为true,高度从match_parent改为wrap_content,恢复自身滚动能力 - 给最外层NestedScrollView保留
app:layout_behavior="@string/appbar_scrolling_view_behavior"配置(如果用了CollapsingToolbarLayout的话),开启android:nestedScrollingEnabled="true"
- 将原本和NestedScrollView平级的RecyclerView移入NestedScrollView内部,作为垂直布局的子项,同时给RecyclerView设置
- 补充WebView触摸事件配置
- 给WebView设置触摸监听,按下时请求父容器暂时不要拦截事件,优先响应WebView内部滚动,抬起/取消时恢复父容器拦截能力
- 开启WebView垂直滚动条,明确自身滚动边界
修正后代码
布局代码
<androidx.core.widget.NestedScrollView android:id="@+id/webScrollContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:nestedScrollingEnabled="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:visibility="@{playerType == PlayerViewType.WEB ? View.VISIBLE : View.GONE}" tools:visibility="gone"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <WebView android:id="@+id/previewWebView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:isScrollContainer="true" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/bottomListRv" android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="false"/> </LinearLayout> </androidx.core.widget.NestedScrollView>
WebView初始化代码
@SuppressLint("SetJavaScriptEnabled", "JavascriptInterface") private fun initWebView() { web.let { with(it.settings) { javaScriptEnabled = true userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Safari/537.36" domStorageEnabled = true loadWithOverviewMode = true useWideViewPort = true mediaPlaybackRequiresUserGesture = true allowFileAccess = true cacheMode = WebSettings.LOAD_DEFAULT } // 开启垂直滚动条 it.isVerticalScrollBarEnabled = true it.isHorizontalScrollBarEnabled = false if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { it.setLayerType(View.LAYER_TYPE_HARDWARE, null) } it.webChromeClient = WebChromeProgressClient() it.webViewClient = WebClient() // 触摸时优先响应WebView内部滚动 it.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> v.parent.requestDisallowInterceptTouchEvent(true) MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> v.parent.requestDisallowInterceptTouchEvent(false) } false } } }
注意:如果WebView加载的是超长H5页面,
wrap_content高度会导致WebView一次性渲染全部内容,触发内存占用过高问题,这种场景可以自定义实现支持嵌套滑动的WebView,重写onMeasure方法限制最大高度,配合NestedScrollView滑动逻辑实现分批渲染即可。
内容的提问来源于stack exchange,提问作者Viacheslav Kolisnichenko
相关产品推荐
相关产品推荐

