【发布时间】:2020-04-17 15:22:40
【问题描述】:
我的主布局中有根容器的motionLayout。在里面,还有其他的景色。其中之一是 frameLayout,包含一个片段。该片段是一个页面,由一个 NestedScrollView 等组成...... MotionLayout 的 OnSwipe 仅用于水平滑动,而 NestedScrollView 应该只能垂直滚动。我必须扩展 MotionLayout onInterceptTouchEvent 方法(查看下面的代码),并且只将那些非水平的触摸事件传递给孩子。到目前为止非常简单,并且可以正常工作。
问题是,当我开始在吸收某种触摸事件的视图(如按钮或 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 ! (这就是我正在处理的问题行为存在的情况)并且如果我在应用程序启动时没有刷任何东西(虽然没有问题尚未),请点击一个按钮,我在 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 版本的约束布局。
【问题讨论】:
-
遇到完全相同的问题。偶然发现,当我开始在消耗触摸的元素上滑动时,比如 recyclerview,我的触摸区域奇怪地转移到了一些不相关的位置,比如屏幕右上角没有触摸吸收元素。从那里刷卡工作。
-
@frangulyan 你找到解决方案了吗?
-
你为什么使用 motion:touchRegionId="@id/view_options" ?特别是因为您正在覆盖 onInterceptTouchEvent。一般使用limitBoundsTo
标签: android android-constraintlayout android-touch-event android-motionlayout