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.
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:
RecyclerView
RecyclerView
items
FrameLayout
click events when RecyclerView's
empty spaces are clickedRecyclerView's
empty spaces
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With