Search code examples
androidonclicklistenerandroid-constraintlayoutandroid-motionlayout

MotionLayout prevents ClickListener on all Views


I am using a MotionLayout with a scene-xml:

<Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    >
    <OnSwipe
        motion:touchAnchorId="@+id/v_top_sheet"
        motion:touchRegionId="@+id/v_top_sheet_touch_region"
        motion:touchAnchorSide="bottom"
        motion:dragDirection="dragDown" />
</Transition>

The 2 ConstraintSets are referencing only 2 View IDs: v_notifications_container and v_top_sheet.

In my Activity I want to set a normal ClickListener to one of the other Views in this MotionLayout:

iv_notification_status.setOnClickListener { Timber.d("Hello") }

This line is executed, but the ClickListener is never triggered. I searched other posts, but most of them deal with setting a ClickListener on the same View that is the motion:touchAnchorId. This is not the case here. The ClickListener is set to a View that is not once mentioned in the MotionLayout setup. If I remove the app:layoutDescription attribute, the click works.

I also tried to use setOnTouchListener, but it is also never called.

How can I set a click listener within a MotionLayout?


Solution

  • small modification of @TimonNetherlands code that works on the pixel4 aswell

    class ClickableMotionLayout: MotionLayout {
        private var mStartTime: Long = 0
        constructor(context: Context) : super(context)
    
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    
        override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
    
            if ( event?.action == MotionEvent.ACTION_DOWN ) {
                mStartTime = event.eventTime;
            }
    
            if ((event?.eventTime?.minus(mStartTime)!! >= ViewConfiguration.getTapTimeout()) && event.action == MotionEvent.ACTION_MOVE) {
                return super.onInterceptTouchEvent(event)
            }
    
            return false;
        }
    }