constraintlayout icon indicating copy to clipboard operation
constraintlayout copied to clipboard

How to add click listener to Carousel item

Open litao0621 opened this issue 3 years ago • 5 comments

I'm having trouble adding an onClickListener to my carousel's items. I've tried setting the onClickListener in the adapter like so:

carousel.setAdapter(object : Carousel.Adapter {
  override fun populate(view: View?, index: Int) {
    view.setOnClickListener {
      // do something
    }
  }
})

However, this makes the carousel unresponsive to swiping. When I try to swipe the carousel it executes my onClick function but doesn't execute my onSwipe transition.

Would really appreciate any help!

litao0621 avatar May 30 '22 10:05 litao0621

I made several changes based on MotionLayout.It worked for me right now

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.constraintlayout.motion.widget.MotionLayout
import kotlin.math.abs

/**
 * @author : litao
 * @email  : [email protected]
 * @date   : 2022/5/31 5:31 
 */
class TestMotionLayout constructor(
    context: Context,
    attrs: AttributeSet?,
    defStyleAttr: Int,
) : MotionLayout(context, attrs, defStyleAttr) {

    private var mInitX = 0f
    private var mInitY = 0f

    private var mTouchSlop = 10

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {

        when (ev.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                mInitX = ev.x
                mInitY = ev.y
            }
            MotionEvent.ACTION_MOVE -> {
                val moveX = abs(ev.x - mInitX)
                val moveY = abs(ev.y - mInitY)

                if (moveX > mTouchSlop || moveY > mTouchSlop){
                 
                    val obtain = MotionEvent.obtain(ev)
                    obtain.action = MotionEvent.ACTION_DOWN
                    dispatchTouchEvent(obtain)
                    onTouchEvent(obtain)
                    return true
                }
            }
            MotionEvent.ACTION_UP -> {
            }
        }
        return false
    }


}

Could you please supply a better solution to this problem

litao0621 avatar Jun 01 '22 02:06 litao0621

Use this snippet if you place your carousel as a row item in a recyclerview.

override fun onInterceptTouchEvent(event: MotionEvent?): Boolean = true

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                previousX = event.x.toInt()
                previousY = event.y.toInt()
                hasActionMoveOccurred = false
            }
            MotionEvent.ACTION_UP -> {
                disableTouchEventsForParent(false)

                if (hasActionMoveOccurred.not()) {
                    onClick(carousel.currentIndex)
                    return true
                }
            }
            MotionEvent.ACTION_MOVE -> {
                val xDifferenceAbsolute = abs(calculateDistanceX(event))
                val yDifferenceAbsolute = abs(calculateDistanceY(event))
                hasActionMoveOccurred = xDifferenceAbsolute > scaledTouchSlop || yDifferenceAbsolute > scaledTouchSlop

                if (isHorizontallyScrolling) {
                    return super.onTouchEvent(event)
                }

                val scrolledHorizontally = xDifferenceAbsolute > yDifferenceAbsolute
                disableTouchEventsForParent(scrolledHorizontally)
            }
        }
        return super.onTouchEvent(event)
    }

    private fun disableTouchEventsForParent(disable: Boolean) {
        isHorizontallyScrolling = disable
        parent.requestDisallowInterceptTouchEvent(disable)
    }

    private fun calculateDistanceX(currentMotionEvent: MotionEvent): Int = previousX - currentMotionEvent.x.toInt()
    private fun calculateDistanceY(currentMotionEvent: MotionEvent): Int = previousY - currentMotionEvent.y.toInt()

You could do it like that, but if a user clicks while the "swipe motion" is in progress, it will trigger the onClick call on the old currentIndex of the carousel. So basically you can't differentiate which item was clicked.

Would be nice, if there was a ItemClickListener for the carousel. @jafu888

vassilisimon avatar Jun 07 '22 14:06 vassilisimon

I am trying to figure out what sort of attribute we should add to enable the behavior you want. I do not want to modify the default behavior.

For Carousel index: I do not want to make assumptions on what is in the Carousel.

The simplest way to know what you clicked on is to setTag("MY_INDEX" , index) on the view in when populate it.

jafu888 avatar Jun 08 '22 22:06 jafu888

The easiest solution for now was to ignore clicks while the motionlayout is in transition.

vassilisimon avatar Jun 11 '22 17:06 vassilisimon

Any updates on this issue. Please recommend any solution.

prasanthaviator avatar Dec 13 '23 06:12 prasanthaviator