Подтвердить что ты не робот

Recyclerview: прослушивание событий с добавлением кликов

У меня есть горизонтальный RecyclerView с leftPadding = 48dp, topPadding = 24dp и clipToPadding = false. Он начинается с пустого пространства слева, но когда пользователь прокручивает список, его элементы рисуются на этом (ранее пустое) пространстве. Верхнее пространство всегда пусто.

введите описание изображения здесь

Этот RecyclerView находится внутри FrameLayout с foreground = selectableItemBackground.

Моя проблема связана с тем, что RecyclerView потребляет и игнорирует касания в левом и верхнем пространствах, что означает, что OnClickListener не будет запущен, как при подключении к FrameLayout, так и к RecyclerView.

Я уже пробовал с clickable = false и focusable = false на RecyclerView, он не работает.

Что я ищу:

  • Прокручиваемый RecyclerView введите описание изображения здесь
  • Доступные для использования элементы RecyclerView введите описание изображения здесь
  • FrameLayout нажмите события, когда RecyclerView's нажата пустое пространство
  • (альтернатива 3) Clickable RecyclerView's пустое пространство введите описание изображения здесь введите описание изображения здесь

EDIT: Я создал простой проект, который показывает проблему, о которой я говорю: https://github.com/dadino/recyclerviewemptyspacestest Есть 2 коммита, на первом я пытаюсь поймать щелчок на родительском представлении, а во втором я пытаюсь поймать щелчок по самому RecyclerView. Ни один из них не работает.

4b9b3361

Ответ 1

Вам нужно создать свою собственную реализацию 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())
    }

}

Результат:

введите описание изображения здесь