title: Using MotionLayout OnSwipe on Item of a RecyclerView
body: <p>I try to achieve an animation on an item of my recycler view, a custom swipe, and for that, I tried to use the MotionLayout. 
  Actually, it works fine if the trigger of the animation is a click, but when I want to put a swipe, it interferes with the swipe of the recycler view. Here's my scene :</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;MotionScene
      xmlns:motion="http://schemas.android.com/apk/res-auto"&gt;
  
  &lt;Transition
      motion:constraintSetStart="@layout/chat_item_start"
      motion:constraintSetEnd="@layout/chat_item_end"
      motion:duration="500"&gt;
  
      &lt;OnClick
          motion:targetId="@+id/background"
          motion:clickAction="toggle" /&gt;
  
      &lt;!-- &lt;OnSwipe
          motion:touchAnchorId="@+id/background"
          motion:touchAnchorSide="right"
          motion:dragDirection="dragLeft" /&gt; --&gt;
  
      &lt;/Transition&gt;
  
  &lt;/MotionScene&gt;
  </code></pre>
  
  <p>How look my list at the beginning : </p>
  
  <p><a href="https://i.stack.imgur.com/efB2c.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/efB2c.png" alt="enter image description here"></a></p>
  
  <p>And at the end, when I click on an item : </p>
  
  <p><a href="https://i.stack.imgur.com/BO4WH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/BO4WH.png" alt="enter image description here"></a></p>
  
  <p>When I switch to OnSwipe trigger, the animation works but only if I strictly swipe horizontally, if, by mistake, my finger go a little up or down, the animation is stopped and don't even finish itself : </p>
  
  <p><a href="https://i.stack.imgur.com/8kE0Q.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8kE0Q.png" alt="enter image description here"></a></p>
  
  <p>I think the problem comes because the recycler view also handles the swipe gesture, because when the animation stops (if I go a little up/down) the recycler moves as usually. </p>
  
  <p>If I play too much with the swipe, I get this error : </p>
  
  <blockquote>
    <p>java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.VelocityTracker.addMovement(android.view.MotionEvent)' on a null object reference
            at androidx.constraintlayout.motion.widget.MotionLayout$MyTracker.addMovement(MotionLayout.java:985)
            at androidx.constraintlayout.motion.widget.MotionScene.processTouchEvent(MotionScene.java:1043)
            at androidx.constraintlayout.motion.widget.MotionLayout.onTouchEvent(MotionLayout.java:2992)
            at android.view.View.dispatchTouchEvent(View.java:12515)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3024)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2705)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
            at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
            at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:444)
            at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1829)
            at android.app.Activity.dispatchTouchEvent(Activity.java:3397)
            at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
            at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:402)
            at android.view.View.dispatchPointerEvent(View.java:12754)
            at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5052)
            at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4855)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4372)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4425)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4391)
            at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4531)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4399)
            at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4588)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4372)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4425)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4391)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4399)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4372)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7034)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7007)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6968)
            at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7137)
            at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:186)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:326)
            at android.os.Looper.loop(Looper.java:142)
            at android.app.ActivityThread.main(ActivityThread.java:6649)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)</p>
  </blockquote>
  
  <p>I have no idea how to properly dispatch the touch event between the MotionLayout and the RecyclerView, and if it's even possible at this stage, with the motion layout.</p>
  
  <p>If anyone has an idea, thanks in advance! </p>
  
  <p>UPDATE : </p>
  
  <p>Here's the code to handle the touch event (disable/enable on the recyclerView) </p>
  
  <p>The onTouchListener on the recyclerView Item</p>
  
  <pre><code>holder.itemView.setOnTouchListener { view, motionEvent -&gt;
  
          val progress = holder.motion.progress
  
          when(motionEvent.action){
              MotionEvent.ACTION_DOWN -&gt; {
                  x = motionEvent.x
                  y = motionEvent.y
                  Log.d("Event Down", "X : $x, Y : $y")
              }
  
              MotionEvent.ACTION_UP-&gt;{
                  finalx = motionEvent.x
                  finaly = motionEvent.y
                  Log.d("Event Up", "X : $finalx, Y : $finaly")
  
                  if(progress &gt; 0.5 &amp;&amp; progress != 1.0F ){
                      holder.motion?.transitionToEnd()
                  }else{
                      holder.motion?.transitionToStart()
                  }
                  holder.itemView.parent.requestDisallowInterceptTouchEvent(false)
              }
              MotionEvent.ACTION_MOVE -&gt;{
                  val currentX = motionEvent.x
                  val currentY = motionEvent.y
  
                  val deltaX = abs(x - currentX)
                  val deltaY = abs(y - currentY)
  
  
                  if (deltaX &gt; 1 &amp;&amp; deltaY &gt; 1 &amp;&amp; deltaX &gt; deltaY ){
                      holder.itemView.parent.requestDisallowInterceptTouchEvent(true)
                  }
                  Log.d("Event Move", "X : $currentX, Y : $currentY")
              }
          }
          false
      }
  </code></pre>
  
  <p>The transition listener</p>
  
  <pre><code>holder.motion.setTransitionListener(object: MotionLayout.TransitionListener{
  
          override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {}
          override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
              Log.d("Animation started", "Start id : $p1")
              holder.itemView.parent.requestDisallowInterceptTouchEvent(true)
          }
  
          override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
              Log.d("Animation change ", "progress : $p3")
          }
  
          override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
              val progress = holder.motion.progress
              opened = progress == 1.0f
              Log.d("Animation Completed", "progress : $progress")
              holder.itemView.parent.requestDisallowInterceptTouchEvent(false)
          }
      })
  </code></pre>
  
  <p>I use <code>requestDisallowInterceptTouchEvent</code> a bit everywhere because I've noticed different behavior and this code is the one that works the best, but still have the crash after playing around with the cells/scroll/animation.</p>