Android中SwipeRefreshLayout调用addView时LayoutParams不生效
你设置的LayoutParams高度不生效,是被SwipeRefreshLayout的两层逻辑拦截覆盖了,和addView的传参语法无关。
第一层:addView流程的LayoutParams类型校验直接丢弃参数
所有ViewGroup在执行addView操作时,都会先校验传入的LayoutParams类型是否是当前容器支持的类型,校验不通过就会重新生成自身适配的布局参数实例,你传入的自定义参数会被直接丢弃。
你现在传入的是最基础的ViewGroup.LayoutParams,而SwipeRefreshLayout的checkLayoutParams方法只认自身定义的LayoutParams类型,校验失败后会调用generateLayoutParams方法生成默认布局参数,默认宽高都是MATCH_PARENT,你设置的height=1在这一步就已经失效。
可以在addView之后加一行日志验证:
Log.d("LayoutParamsCheck", "webView height after add: " + webView.getLayoutParams().height);
打印出来的高度值一定是-1(即MATCH_PARENT的常量值),不是你传入的1。
第二层:onMeasure测量逻辑强制覆盖子View高度
哪怕你通过反射强行把webView的layoutParams高度改成1,最终渲染时WebView还是会占满全屏——因为SwipeRefreshLayout本身的设计逻辑就要求唯一直接子View占满自身全部可用高度。
查看SwipeRefreshLayout的onMeasure源码就能看到,它测量唯一子View的时候,会直接把父容器自身的可用高度作为精确测量高度传给子View,根本不会读取子View LayoutParams里配置的height值,相当于在测量阶段直接把你设置的高度覆盖了。
SwipeRefreshLayout是专门实现下拉刷新交互的容器控件,本身只支持一个直接子View,这个子View必须是可滚动的内容控件(比如WebView、RecyclerView、NestedScrollView),下拉刷新的触发判断完全依赖子View的滚动位置回调。如果子View高度只有1px,根本无法产生滚动事件,下拉刷新逻辑会完全失效,这是控件本身的设计约束,不是bug。
如果你确实需要控制WebView的高度,不要直接把WebView作为SwipeRefreshLayout的直接子View,中间加一层基础布局做中转即可:
// 先给SwipeRefreshLayout加一个占满全高的中间容器 FrameLayout refreshChildContainer = new FrameLayout(swipeToRefresh.getContext()); swipeToRefresh.addView(refreshChildContainer, new SwipeRefreshLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT )); // 再把WebView加到中间容器中,此时用FrameLayout.LayoutParams设置高度就会正常生效 refreshChildContainer.addView(webView, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 1 ));
注意该方案下WebView高度仅1px,SwipeRefreshLayout的下拉刷新能力会失效,如果需要正常使用下拉刷新功能,不要修改直接子View的高度,遵循控件的设计使用规范即可。
内容的提问来源于stack exchange,提问作者Patroclus

