I have a motionLayout for the root container, in my main layout. Inside it, there are other views. One of them is a frameLayout, containing a fragment. The fragment is a page, consisting a NestedScrollView etc... MotionLayout has OnSwipe for only horizontal swipes, and the NestedScrollView should only be able to be scrolled vertically. I had to extend the MotionLayout onInterceptTouchEvent method (look at the code below) and only pass to children those touch events which were not horizontal. Pretty straightforward so far and it works.
The problem is, when I start swiping on views which absorb some kind of touch events (like buttons, or the NestedScrollView) it messes up the motionLayout transitions and it skips frames instantaneously, so the anchor corresponds with my mouse position. The transition progresses almost completely in a frame and kills the UI experience. My guess is, if the motionLayout takes X1 and X2 for the transitions, the X1 value is the one casuing the problem and its value coming from where it's not supposed to. But of course when I start swiping on touch absorbing elements.
This is the override method of OnInterceptTouchEvent of my customMotionLayout:
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
super.onInterceptTouchEvent(event)
return when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
previousX = event.x.toInt()
previousY = event.y.toInt()
mIsScrolling = false
false
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
mIsScrolling = false
false
}
MotionEvent.ACTION_MOVE -> {
if (mIsScrolling) true
else {
var xMotion: Int = abs(previousX - event.x.toInt())
var yMotion: Int = abs(previousY - event.y.toInt())
previousX = event.x.toInt()
previousY = event.y.toInt()
if (xMotion >= yMotion) {
mIsScrolling = true
true
} else false}}
else -> {
mIsScrolling = false
false
}
}
}
This is the relevant transition:
<Transition android:id="@+id/options_to_now" motion:motionInterpolator="easeInOut"
motion:pathMotionArc="none"
motion:constraintSetStart ="@id/options_state"
motion:constraintSetEnd="@id/now_state"
motion:duration="100">
<OnSwipe
motion:maxAcceleration="100"
motion:maxVelocity="100"
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/view_options"
motion:touchAnchorSide="left"
motion:touchRegionId="@id/view_options"/>
</Transition>
I made sure with testing that there is no problem with MotionDescripton and layouts.
It's noteworthy that the motion works perfectly when I don't touch on elements which absorb some kind of touch events. Like blank spaces in my layout, and it only causes mentioned problem when I swipe over those elements.
Maybe If I knew how motionLayout transitions are related to touchPositions, I could fix it.
UPDATE 1: I noticed if I click on a blank space (simple click ACTION_DOWN), and then swipe, even starting on those naughty elements, the problem differs. That click somehow updates the coordination positions of the transition. It takes where I click as the starting point, for example x1. Then when I swipe over something that absorbs touch events, it gets its x2 from there. Now what I'm seeing in the exact next frame is like the result as if I swiped the motion, from x1 to x2.
UPDATE 2: Another thing I've noticed which I think is pretty much related to my problem, is that the transition always is in the STARTED state, when I'm done swiping. Like I swipe from x1 to x2, its states goes from started, completed and then started. So I guess when my next swipe appears, it thinks my fingers were on the touch all the time and I manually siwped, which I didn't. The result in the log after listening to the transition and prining the states are as follows, when I do a complete cycle of swiping geture and finishig it while there is no fingers on the screen. So the last event that is called is started, which is not logical to me.
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.014359029
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.02863888
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.044761233
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.06311959
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.09285617
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.12685902
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.17269236
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.22314182
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.27269235
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.32545927
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.389359
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.4449254
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.44595188
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.4948284
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.57895637
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.6884479
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.814522
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.8918733
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.95612586
D/e: Changed, state = -1 / p1 = 2131230901 / p2 = 2131230907 / p3 = 0.9949746
D/e: Completed: state = 2131230907 / p1 = 2131230907
D/e: Started, state = 2131230907 / p1 = 2131230901 / p2 = 2131230907
Changed, state = 2131230907 / p1 = 2131230901 / p2 = 2131230907 / p3 = 1.0
UPDATE 3: Another thing I've noticed is that, if and only if a transition successfully changes state of the motion, we come to a situation where by clicking on one of those touch absorbing elements, motionLayout doesn't recieve OnTouchEvent! (and this is the situation, in where the problematic behaviour I'm dealing with exist) and If I don't swipe anything when the application starts (while there is no problem yet), by clicking on a button, I recieve ACTION_DOWN and ACTINO_UP in the motionLayout. So whatever happens in the motionLayout after a transition to a new state, it prevents the motionLayout to recieve OnTouchEvents from the children.
UPDATE 4: I'm not very familiar with how MotionLayout handles scene creation, but if someone knows, could it be that motionLayout creates a scene dynamicaly which somehow prevents onTouchEvent travels all the way back up to the parents?
UPDATE 5: So when the motionEvent with ACTION_DOWN is not being recieved by the motionLayout, the problem arises. I've looked the call stack and figured it should be related to the view absorbing event dispatchTouchEvent method, returning true for the ACTION_DOWN when start swiping on touch absorbing elements, and returning false for other cases. So, returning true in dispatchTouchEvent causes the motionLayout not recieving the ACTION_DOWN and causing the problems. Inside the dispatchTouchEvent, the code that diffes the results for two situations is the part:
if (onFilterTouchEventForSecurity(event)) {
...
if (!result && onTouchEvent(event)) {
result = true;
}
}
the OnTouchEvent(event) returns true when clicking on buttons for example and false when clicking on blank spaces.
I'm using 2.0.0-beta4 version of Constraint Layout.