androidkotlinandroid-layoutandroid-motionlayout

How to add click listener to Carousel item


I've created a Carousel using MotionLayout by following this guide: https://developer.android.com/training/constraint-layout/motionlayout/carousel

I've gotten everything from this guide working - I can swipe through a carousel of items.

However, 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.

My question is, what is the proper way to set an onClick function for a Carousel item?

While searching for an answer, I've seen a few posts that reference Carousel.setOnItemClickListener(). This is exactly what I need, but it seems to have been deprecated.

I've also seen posts to say to override onTouchListener and run your onClick code on the ACTION_UP event. I've tried a couple variations of this code snippet, but haven't been able to get it to work:

carousel.setAdapter(object : Carousel.Adapter {
  override fun populate(view: View?, index: Int) {
    view.setOnTouchListener { view, motionEvent ->
      if(motionEvent.action == MotionEvent.ACTION_UP) {
        // do something
      }
      view.performClick()
    }
  }
})

Would really appreciate any help!


Solution

  • No better solution

    You could have override parent MotionLayout class like this.Intercept the slide event

    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  : onresume@live.com
     * @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
        }
    
    
    }
    

    Worked for me