返回

子 RecyclerView 滚动触发 onRefresh:深入分析与有效解决方案

Android

当子 RecyclerView 滚动时,活动上的 onRefresh 方法被调用:深入剖析与解决方案

问题

当你遇到当滚动子 RecyclerView 时,活动上的 onRefresh 方法被调用的问题,首先需要理解背后的根源。

问题根源:

RecyclerView 是一个可滚动的视图,当用户滑动它时,会触发 onTouchEvent 事件。同时,活动上实现的 SwipeRefreshLayout 组件也监听触摸事件,当检测到下拉刷新手势时,会触发 onRefresh 方法。当子 RecyclerView 嵌套在 SwipeRefreshLayout 中时,这会导致触摸事件冲突,从而导致子 RecyclerView 的滚动事件意外触发活动上的 onRefresh 方法。

解决方案:

为了解决这个问题,我们需要阻止子 RecyclerView 将触摸事件传递给父 SwipeRefreshLayout。这可以通过向子 RecyclerView 添加一个 onInterceptTouchEvent 监听器来实现。该监听器负责拦截触摸事件,并防止它们传播到父视图。

代码实现:

val scrollTouchListener: RecyclerView.OnItemTouchListener = object :
        RecyclerView.OnItemTouchListener {
    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        when (e.action) {
            MotionEvent.ACTION_UP -> rv.parent.requestDisallowInterceptTouchEvent(true)
            MotionEvent.ACTION_DOWN -> rv.parent.requestDisallowInterceptTouchEvent(true)
            MotionEvent.ACTION_MOVE -> rv.parent.requestDisallowInterceptTouchEvent(true)
        }
        return false
    }

    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
    }

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
    }
}
mBinding.childRecyclerView.addOnItemTouchListener(scrollTouchListener)

此代码将一个 onItemTouchListener 添加到子 RecyclerView,它拦截了三种主要触摸事件:ACTION_UP、ACTION_DOWN 和 ACTION_MOVE。当这些事件发生时,它调用 requestDisallowInterceptTouchEvent 方法,指示父视图(SwipeRefreshLayout)不要拦截触摸事件。这将防止子 RecyclerView 的滚动触发活动上的 onRefresh 方法。

其他注意事项:

  • 确保 onInterceptTouchEvent 方法始终返回 false,以允许子 RecyclerView 处理触摸事件。
  • 如果子 RecyclerView 有多个可滚动的视图,则需要为每个视图添加 onItemTouchListener。
  • 为了提高性能,可以考虑只在需要时拦截触摸事件。例如,当子 RecyclerView 达到顶部或底部时,就不需要拦截触摸事件。

结论:

通过添加一个 onInterceptTouchEvent 监听器,我们可以阻止子 RecyclerView 将触摸事件传递给父 SwipeRefreshLayout,从而解决当子 RecyclerView 滚动时触发 onRefresh 方法的问题。此解决方案有效且易于实现,可以防止触摸事件冲突并确保正确处理滚动和刷新事件。

常见问题解答:

  1. 为什么会出现触摸事件冲突?
    答:由于子 RecyclerView 和父 SwipeRefreshLayout 都监听触摸事件,当用户滑动子 RecyclerView 时,会出现冲突。

  2. 除了添加 onItemTouchListener 之外,还有其他解决方法吗?
    答:另一种方法是修改 SwipeRefreshLayout 的布局参数,使其高度固定或不可滑动。

  3. onItemTouchListener 中的 requestDisallowInterceptTouchEvent 方法有什么作用?
    答:该方法告诉父视图(SwipeRefreshLayout)不要拦截触摸事件。

  4. 我如何提高性能?
    答:可以考虑只在需要时拦截触摸事件,例如当子 RecyclerView 达到顶部或底部时。

  5. 子 RecyclerView 中有多个可滚动的视图怎么办?
    答:需要为每个可滚动的视图添加一个 onItemTouchListener。