Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recyclerview: listen to padding click events

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.

like image 506
David Corsalini Avatar asked Oct 25 '17 10:10

David Corsalini


1 Answers

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

like image 60
azizbekian Avatar answered Oct 21 '22 18:10

azizbekian