SwipeRefreshLayout 嵌套 RecyclerView 在下滑上滑的时候,有时会互相冲突。
先说网上的解决方式是:
给 RecyclerView 添加滑动监听(addOnScrollListener),在onScrolled里获取第一个item的top,当上下滑动时top只为负数,当第一个item完全展示出来时top为0。
当top为0时。说明滑动到头了,再把SwipeRefreshLayout的Enabled设置为true......
RecyclerView 的id是 home_recyclerView,SwipeRefreshLayout 的id是 home_refresh
home_recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
LogUtils.e("onScrollStateChanged = ")
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
LogUtils.e("onScrolled = ")
if (recyclerView != null && recyclerView.getChildCount() > 0) {
val firstChild = recyclerView.getChildAt(0)
val firstChildPosition = if(firstChild == null) 0 else firstChild.top
LogUtils.e("onScrolled = "+ firstChild.top)
//如果firstChild处于列表的第一个位置,且top>=0,则下拉刷新控件可用
home_refresh.setEnabled(firstChildPosition >= 0)
}
}
})
不足是:
但这种情况在RecyclerView 的高度是相对固定时才是起作用的,如果RecyclerView高度是wrap_content上述方法不起作用.....recyclerView.getChildAt(0).top为0时不管是不是第一个item,都会触发下拉刷新.....
我的解决方式是:
先看布局:
项目里SwipeRefreshLayout嵌套ScrollView,ScrollView里面在放RecyclerView,RecyclerView高度是wrap_content。
上方是个布局的头部(绿色),往下依次是滚动消息view(浅绿色),banner,以及空白的padding,最下方是RecyclerView。
要实现的效果是,除了布局的头部,剩下的都要能滑动,当消息view都展示出来后再使SwipeRefreshLayout 启动。
解决原理是:
当RecyclerView绘制完成后计算出第一个item到布局头部(深绿色)的距离y,当RecyclerView 可见 的第一个item 为 0时,并且可见的第一个item 距离头部距离大于等于之前的y时,是的话再设置SwipeRefreshLayout可用~
(计算距离时使用的是坐标)
具体代码:
var rvFirstItemDistanceHomeHead: Int = 0 //记录view完成时第一个item 距离home_head 底部的距离
home_recyclerView.viewTreeObserver.addOnGlobalLayoutListener {
LogUtils.e("addOnGlobalLayoutListener = ")
//RecyclerView绘制完成。测量可见的第一个item 距离home_head 底部的距离
if(rvFirstItemDistanceHomeHead == 0){
val distanceFirstItem = getDistanceFirstItem(refuseMarket)
val firstChildY = distanceFirstItem.get(0)
val headLocationY = distanceFirstItem.get(1)
if ((firstChildY - headLocationY) > 0) rvFirstItemDistanceHomeHead =
(firstChildY - headLocationY)
LogUtils.e("rvFirstItemDistanceHomeHead = " + rvFirstItemDistanceHomeHead)
}
}
home_recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
//LogUtils.e("onScrollStateChanged = " + if (newState == 0) "没有滚动" else if (newState == 1) "被外部拖拽" else "自动滚动")
LogUtils.e("onScrollStateChanged = ")
//判断可见的第一个item是不是第一个item
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val firstCompletelyVisibleItemPosition =
layoutManager!!.findFirstCompletelyVisibleItemPosition()
LogUtils.e("onScrollStateChanged = " + firstCompletelyVisibleItemPosition)
//实时判断可见的第一个item 距离home_head 底部的距离
val distanceFirstItem = getDistanceFirstItem(recyclerView)
val firstChildY = distanceFirstItem.get(0)
val headLocationY = distanceFirstItem.get(1)
LogUtils.e("onScrolled = " + (firstChildY - headLocationY))
//当RecyclerView 可见 的第一个item 为 0(为RecyclerView 的第一个数据)时,并且可见的第一个item 距离头部距离大于等于之前的y时,设置为可用
home_refresh.setEnabled((firstCompletelyVisibleItemPosition == 0) && (firstChildY - headLocationY >= rvFirstItemDistanceHomeHead))
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
LogUtils.e("onScrolled = ")
}
})
其中的getDistanceFirstItem方法为:
private fun getDistanceFirstItem(recyclerView: RecyclerView): ArrayList {
val distanceList = ArrayList()//用于存储recycleView第一个可见item 顶部的Y坐标、 头部view底下 的Y坐标
if (recyclerView != null && recyclerView.getChildCount() > 0) {
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val firstCompletelyVisibleItemPosition =
layoutManager!!.findFirstCompletelyVisibleItemPosition()
val firstChild =
layoutManager.findViewByPosition(firstCompletelyVisibleItemPosition)
var firstChildY = 0//recycleView第一个可见item 顶部的Y坐标
var headLocationY = 0//头部view底下 的Y坐标
if (firstChild != null) {
//获取坐标
val firstChildLocation = IntArray(2)
firstChild.getLocationInWindow(firstChildLocation)
// firstChildLocation[0]//x坐标
firstChildY = firstChildLocation[1] // y 坐标
}
//获取坐标
val headLocation = IntArray(2)
home_head.getLocationInWindow(headLocation)
// firstChildLocation[0]//x坐标
headLocationY = headLocation[1] // y 坐标
distanceList.add(firstChildY)
distanceList.add(headLocationY)
}
return distanceList
}
主要代码就完成了。这个在recycleView 高度是固定的时也是有效的。
另外:
控制SwipeRefreshLayout是否可用是在onScrollStateChanged()方法里处理的,因为recycleView高度是wrap_content的,还嵌套了ScrollView,在滑动时是不会走onScrolled()的。
即使recycleView高度固定,只有当数据展示超过一屏滑动时才会调onScrolled(),一屏就能展示下时滑动也是不会调onScrolled(),应该是这样的吧。。。。
有不对的还请指教~