constraintlayout icon indicating copy to clipboard operation
constraintlayout copied to clipboard

Touch event interception during swipe

Open MMKrivtsov opened this issue 4 years ago • 10 comments

Currently if there is scrolling view (ex. RecyclerView) inside of MotionLayout, this view grabs touch events preventing transitions with swipe over that view. Method onInterceptTouchEvent can handle and intercept touches if touch region id is set, but in that case it uses onTouchEvent and it returns true even if touch is not swipe yet, preventing interaction with child views.

I suppose it would be good to have (at least option) to return result of this.mScene.processTouchEvent(...) instead of true, which in turn would return result of this.mCurrentTransition.mTouchResponse.processTouchEvent(...) and that one would return value of mDragStarted.

Or at least make more getters for accessing such fields: getter for current scene in MotionLayout, make TouchResponse class public and add getter for mDragStarted in TouchResponse. Maybe getter for mCurrentTransition in MotionScene too, but it can be accessed with bestTransitionFor(), so separate getter is not necessary now.

MMKrivtsov avatar Sep 30 '21 10:09 MMKrivtsov

I am not sure what you are asking for: MotionLayout should only be grabbing swipe if you have an <OnSwipe> and you should be able to Disable it with: <OnSwipe ... nestedScrollFlags="disableScroll" />

jafu888 avatar Sep 30 '21 21:09 jafu888

I have a layout with vertical RecyclerView inside of MotionLayout. By default I can swipe both of them simultaneously and that works worse for me than same RecyclerView inside of ViewPager.

MMKrivtsov avatar Sep 30 '21 23:09 MMKrivtsov

Two things:

  1. You can use MotionLayout inside a ViewPager if it has the behavior you are looking for. Listen to The view pager and set progress. (https://developer.android.com/training/constraint-layout/motionlayout/examples#viewpager)

  2. Still not sure the behavior you are asking for. In general I do not recommend using touchRegionId its behavior is unpredictable. (But cannot be change because legacy)

jafu888 avatar Sep 30 '21 23:09 jafu888

Link you provided shows how to use MotionView alongside of ViewPager, not inside. of it. Would not swiping of view pager move child MotionLayout out of view?

If you look inside of HorizontalScrollView class method onInterceptTouchEvent - it does return whether it is being swiped or not, not true as MotionLayout. does. I ask for MotionLayout to work similarly by returning current transition's touch response's value of mDragStarted instead of true in 'onInterceptTouchEvent`. This way it works as expected, but i have to use some reflection calls to achieve this.

For backwards compatibility it can be toggled behavior with additional attribute. By default - return true from onInterceptTouchEvent, if attribute is set - return 'mDragStarted'.

MMKrivtsov avatar Oct 01 '21 09:10 MMKrivtsov

I think I get it. I will leave this open till I fix it.

jafu888 avatar Oct 01 '21 21:10 jafu888

I'm having an issue that's I believe may be closely related to what is being described in this issue.

Given that I have a Motion Layout with a Horizontal RecyclerView. Horizontal Recycler Views seem to prevent MotionLayout transitions from taking place. However, Vertical Recycler Views seems to correctly trigger the MotionLayout transition when scroll interaction takes place.

I've tried using the flag added in commit 9ec3c08, however there is no change to the behavior.

I've created this example project to showcase the issue: Layout: activity_main.xml Motion Scene: motion_layout_scene.xml Activity: (MainActivity.kt)[https://github.com/blad/motion-layout-recycler-view-issue/blob/main/ap

Capture d’écran 2022-10-28 à 3 29 20 PM

blad avatar Oct 28 '22 22:10 blad

RecyclerView's default behavior is to consumes drag events. You can see this by adding a line if (horizontal) rv.suppressLayout(true) to configure. This is probably a know issues with RecyclerView that cannot be fixed with out breaking compatibility. For more information: Bug: RecyclerView should not interfere with opposite direction scroll touch events Example Workaround: Fixing RecyclerView nested scrolling in opposite direction

jafu888 avatar Nov 01 '22 02:11 jafu888

Thank you for your reply John.

Unfortunately the workaround linked does not directly work for my use case.

However it does make it clear that I would possibly need to override MotionLayout's onInterceptTouchEvent method so that I would have any hope of the nested RecyclerView not consuming the event before MotionLayout can react appropriately.

Knowing that, I did find a workaround of wrapping the horizontal RecyclerView in a NestedScrollView with vertical orientation.

I suppose that the NestedScrollView handles the vertical drags and allows the parent to MotionLayout to react correctly.

Leading to something like:

<!-- pseudo-layout xml -->
<MotionLayout>
  <NestedScrollView android:orientation="vertical" android:layout_height="wrap_content"> 
    <RecyclerView /> <!-- Horizontal Orientation -->
  </NestedScrollView>
  <RecyclerView /> <!-- Vertical Orientation -->
</MotionLayout>

activity_mail.xml: Layout with workaround wrapping NestedScrollView

blad avatar Nov 02 '22 19:11 blad

Very cool approach. It works well! I will put a version of that in EamplesRecyclerView. I put examples of how to interact with RecyclerView and ConstraintLayout/MotionLayout.

jafu888 avatar Nov 03 '22 19:11 jafu888

Thank you for your plan. It has been a big help for me. I have already spent two whole days before that

DearZack avatar Jul 04 '23 06:07 DearZack