Recyclerview: listen to padding click events
Asked Answered
G

1

12

I have an horizontal RecyclerView with leftPadding = 48dp, topPadding = 24dp and clipToPadding = false. It starts with an empty space on the left, but when the user scrolls the list its items are drawn on that (previously empty) space. The top space is always empty.

enter image description here

This RecyclerView is inside a FrameLayout with foreground = selectableItemBackground.

My problem comes from the fact that the RecyclerView consumes and ignores touches on the left and top spaces, meaning an OnClickListener won't be triggered, both when attached to the FrameLayout or to the RecyclerView.

I already tried with clickable = false and focusable = false on the RecyclerView, it doesn't work.

What I'm looking for:

  1. Scrollable RecyclerView enter image description here
  2. Clickable RecyclerView items enter image description here
  3. FrameLayout click events when RecyclerView's empty spaces are clicked
  4. (alternative to 3) Clickable RecyclerView's empty spaces enter image description here enter image description here

EDIT: I've created a simple project that shows the problem I'm talking about: https://github.com/dadino/recyclerviewemptyspacestest There are 2 commits, on the first one I try to catch the click on the parent view, on the second one I try to catch the click on the RecyclerView itself. Neither of them works.

Gillispie answered 25/10, 2017 at 10:58 Comment(7)
show your code.Xanthochroid
What code would you like to see?Gillispie
in ur recyclerview adapter, u must have put a global click listner and also the item click listner, make sure u only do that for items. Post code for more detailsCourteous
Each item's view has it's own clickListener, not the adapter (that is not a View and can't register a clickListener). The problem is not item's click listener, but the clicks not registered outside item's bound (where their clickListeners do not have jurisdiction).Gillispie
I've added the link to a simple test project to show my problem.Gillispie
This is what I tell nicely constructed question, I'm enjoying reading it.Camisado
Set margin on top to get the click event on FrameLayout instead of PaddingOssuary
C
14

You have to create your custom RecyclerView implementation, where you would listen to touch events and perform filtering based on that.

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())
    }

}

Result:

enter image description here

Camisado answered 30/10, 2017 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.