下拉刷新与RecyclerView 滑动冲突(SwipeRefreshLayout与RecyclerView 滑动冲突)

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(),应该是这样的吧。。。。

有不对的还请指教~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值