Search code examples
androidandroid-animationandroid-motionlayoutandroid-touch-eventandroid-swipe

MotionLayout - OnSwipe not working on clickable children


I am running into an issue using MotionLayout where a user cannot swipe up/down if they start their swipe on a clickable item. I want the user to be able to swipe over any part of the screen, and the swiping transition is triggered.

Basically, the behavior will be very similar to what is currently on the Yelp app for Android. Swiping up anywhere on the screen moves the search bar to the top of the screen. See this video of the swiping behavior.

My code for the transition looks something like this:

<Transition
    app:constraintSetStart="@id/start"
    app:constraintSetEnd="@id/end"
    >
    <OnSwipe
        app:dragDirection="dragUp"
        app:onTouchUp="decelerate"
        />
</Transition>

My code for the MotionLayout looks something like this:

<androidx.constraintlayout.motion.widget.MotionLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:layoutDescription="@xml/motion_scene">
     >

     <!-- other views -->

     <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >

        <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@string/title"
           android:textColor="@color/black"
           android:textSize="14sp"
           android:lineSpacingExtra="7sp"
           android:fontFamily="@font/avenir"
           android:layout_marginBottom="16dp"
           />

       <GridView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:numColumns="2"
            android:verticalSpacing="8dp"
            android:horizontalSpacing="12dp"
            android:stretchMode="columnWidth"
            tools:listitem="@layout/griditem" <!-- This layout file contains a single <Button/> -->
            />
    </LinearLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>

Here are all of the things I have tried and couldn't get to work:

  1. Overriding onInterceptTouchEvent in the ViewGroup parent of the clickable view (Just returning true here will result in the swiping behavior I want, but then the children views that should be clickable no longer are.)
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
    if (ev?.action == MotionEvent.ACTION_DOWN) {
        onTouchEvent(ev)
        return false
    }
    return true
}
  1. Overriding onTouchEvent in the clickable child view
override fun onTouchEvent(event: MotionEvent?): Boolean {
    if (event?.action == MotionEvent.ACTION_DOWN) {
        super.onTouchEvent(event)
        return false
    }
    return super.onTouchEvent(event)
}
  1. Overriding onInterceptTouchEvent in the MotionLayout
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
    if (event?.action == MotionEvent.ACTION_MOVE) {
        return super.onInterceptTouchEvent(event)
    }
    return false
}

I have looked at all of these StackOverflow posts (and more but these are the most relevant):

Any ideas as to what I could do here to be able to both swipe and click on items?


Solution

  • I got it working. Two things here that helped:

    1. I switched the GridView to a RecyclerView with a GridLayoutManager and set app:touchAnchorId to the id of the RecyclerView.
    2. I actually had two transitions in the MotionScene file, and the other transition (not the OnSwipe) was first. Order matters in the MotionScene file. I switched the order of the two transitions, and I got it to work. However, once the other transition was triggered, it would stop working, so I had to use setTransition to reset the transition back to the OnSwipe transition when appropriate.

    I hope that helps someone who runs into this problem.