Recyclerview: 监听 padding 点击事件

12
我有一个水平的RecyclerView,左内边距为48dp,上内边距为24dp,且clipToPadding = false。它从左侧开始有一个空白区域,但当用户滚动列表时,其项目会绘制在该(之前为空的)区域上。顶部区域始终为空。

enter image description here

这个RecyclerView位于一个带有foreground = selectableItemBackground的FrameLayout中。
我的问题在于RecyclerView会忽略左侧和顶部空间上的触摸操作,这意味着无论将OnClickListener附加到FrameLayout还是RecyclerView上都不会触发。
我已经尝试过在RecyclerView上设置clickable = falsefocusable = false,但并没有起作用。
我正在寻找的是:
  1. 可滚动的RecyclerView enter image description here
  2. 可点击的RecyclerView项目 enter image description here
  3. FrameLayout点击事件,当点击RecyclerView的空白处时
  4. (替代3) 可点击的RecyclerView空白处 enter image description here enter image description here

编辑:我创建了一个简单的项目,展示了我所说的问题: https://github.com/dadino/recyclerviewemptyspacestest 有两个提交,第一个尝试捕获父视图上的点击,第二个尝试捕获RecyclerView本身的点击。都没有成功。


show your code. - HassanUsman
{btsdaf} - David Corsalini
{btsdaf} - Niraj Sanghani
2
这是一个很好的问题,我很喜欢阅读它。 - azizbekian
将顶部的边距设置为点击FrameLayout,而不是填充。 - Samuel Robert
显示剩余2条评论
1个回答

14

你需要创建自定义的RecyclerView实现,监听触摸事件并基于此执行过滤操作。

class MyRecyclerView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    private var isValid = false
    private var x: Int = 0
    private var y: Int = 0
    // this will help us to understand whether the event can be considered a touch or scroll
    private val delta: Int = ViewConfiguration.get(getContext()).scaledTouchSlop

    override fun onTouchEvent(e: MotionEvent?): Boolean {
        val onTouchEvent = super.onTouchEvent(e)
        when (e?.action) {
            MotionEvent.ACTION_DOWN -> {
                // saving initial touch location
                x = e.rawX.toInt()
                y = e.rawY.toInt()
                isValid = true
            }
            MotionEvent.ACTION_MOVE -> {
                if (Math.abs(e.rawX - x) > delta ||
                        Math.abs(e.rawY - y) > delta) {
                    // if a scroll happens - no longer consider this movement as valid
                    // this is needed for handling scroll on the inner part of `RecyclerView`                            
                    isValid = false
                }
            }
            MotionEvent.ACTION_UP -> {
                if (isValid && Math.abs(e.rawX - x) < delta &&
                        Math.abs(e.rawY - y) < delta &&
                        isInRightArea(e)) {
                    // 1. if the movement is still valid
                    // 2. we have actually performed a click                            
                    // 3. the click is in expected rectangle
                    // then perform click
                    performClick()
                }
            }
        }
        return onTouchEvent
    }

    // This is needed in order to handle the edge case, when a click listener 
    // would be fired when performing a click between the items of `RecyclerView`
    private fun isInRightArea(e: MotionEvent): Boolean {
        val r = Rect()
        getGlobalVisibleRect(r)
        r.left = paddingLeft
        r.top = r.top + paddingTop
        return !r.contains(e.rawX.toInt(), e.rawY.toInt())
    }

}

结果:

输入图片描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接