我的主布局中有一个根容器的 MotionLayout。里面还有其他的景色。其中之一是框架布局,包含一个片段。该片段是一个页面,由 NestedScrollView 等组成......
MotionLayout 具有仅水平滑动的 OnSwipe,而 NestedScrollView 应该只能垂直滚动。我必须扩展 MotionLayout onInterceptTouchEvent 方法(请查看下面的代码),并且只将那些非水平的触摸事件传递给子级。到目前为止非常简单it works.
问题是,当我start在吸收某种触摸事件的视图(如按钮或 NestedScrollView)上滑动,它会扰乱 MotionLayout 转换并立即跳过帧,因此锚点与我的鼠标位置相对应。转换几乎完全在一帧内进行,并且破坏了 UI 体验。我的猜测是,如果 MotionLayout 采用 X1 和 X2 进行转换,则 X1 值就是导致问题的值,并且它的值来自不应该出现的地方。当然,当我开始在触摸吸收元素上滑动时。
这是我的 customMotionLayout 的 OnInterceptTouchEvent 的重写方法:
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
}
}
}
这是相关的转换:
<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>
我通过测试确保 MotionDescripton 和布局没有问题。
值得注意的是,当我不触摸吸收某种触摸事件的元素时,该动作可以完美地工作。就像我的布局中的空白区域一样,当我在这些元素上滑动时,它只会导致上述问题。
也许如果我知道 MotionLayout 过渡如何与 touchPositions 相关,我就可以修复它。
更新 1:我注意到,如果我单击空白区域(只需单击 ACTION_DOWN),然后滑动,即使从那些顽皮的元素开始,问题也会有所不同。该单击以某种方式更新了过渡的协调位置。它以我单击的位置为起点,例如 x1。然后,当我在吸收触摸事件的东西上滑动时,它会从那里获得 x2。现在我在下一帧中看到的结果就像我将运动从 x1 滑动到 x2 一样。
更新 2:我注意到的另一件事我认为与我的问题非常相关,那就是当我完成滑动时,转换始终处于 STARTED 状态。就像我从 x1 滑动到 x2 一样,它的状态从开始、完成然后开始。所以我猜当我的下一次滑动出现时,它认为我的手指一直在触摸上并且我手动地滑动,但我没有。当我在屏幕上没有手指的情况下完成一个完整的滑动手势循环并完成它时,听完转换并打印状态后日志中的结果如下。所以最后一个被调用的事件开始了,这对我来说不合逻辑。
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
更新 3:我注意到的另一件事是,当且仅当转换成功更改运动状态时,我们会遇到这样一种情况:通过单击其中一个触摸吸收元素,motionLayout 不会收到 OnTouchEvent! (这就是我正在处理的有问题的行为存在的情况)以及如果我在应用程序启动时不刷任何东西(虽然没有问题)yet),通过单击按钮,我在 MotionLayout 中收到 ACTION_DOWN 和 ACTINO_UP。因此,在转换到新状态后,无论 MotionLayout 中发生什么,它都会阻止 MotionLayout 从子级接收 OnTouchEvents。
更新4:我不太熟悉MotionLayout如何处理场景创建,但如果有人知道,是否motionLayout会动态创建一个场景,从而以某种方式阻止onTouchEvent一路返回到父级?
更新5:因此,当motionLayout未接收到带有ACTION_DOWN的motionEvent时,就会出现问题。我查看了调用堆栈,认为它应该与视图吸收事件dispatchTouchEvent方法有关,当开始在触摸吸收元素上滑动时,ACTION_DOWN返回true,其他情况返回false。因此,在dispatchTouchEvent中返回true会导致motionLayout未接收到ACTION_DOWN并导致问题。在dispatchTouchEvent内部,区分两种情况结果的代码是:
if (onFilterTouchEventForSecurity(event)) {
...
if (!result && onTouchEvent(event)) {
result = true;
}
}
例如,单击按钮时 OnTouchEvent(event) 返回 true,单击空白区域时返回 false。
我正在使用 2.0.0-beta4 版本的约束布局。