[MotionLayout问题,孩子拦截触摸事件

问题描述 投票:2回答:1

我的主布局中有一个用于根容器的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一样,其状态从开始,完成然后开始。因此,我想我的下一次滑动出现时,它会认为我的手指一直处于触摸状态,因此我手动进行了吸吮操作,但没有。当我完成一个完整的擦拭geture并完成操作,而屏幕上没有手指的时候,在听完转换并评估状态之后,日志中的结果如下。因此,最后一个被调用的事件开始了,这对我来说不合逻辑。

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从子级接收OnTouchEvent。]

更新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版本的约束布局。

android android-constraintlayout android-touch-event android-motionlayout
1个回答
0
投票
我想我发现了问题,这是motionLayout的onInterceptTouchEvent中的mRegionView。在该方法的某处,它使用mRegoinView的左,右,顶部和底部检查事件x和y的边界。我查看了这些值,并在那些有问题的情况下看到了这些值有点奇怪,因此该函数返回false,并且从不调用motionLayout的onTouch事件。值是:

良好情况354y 450前0左16底部1280rigt 752

不良情况(如问题所述,当1个过渡已经成功将状态更改为新状态,并且我们开始拖动触摸吸收元件时)

x 300y 450前0剩下750底部1280右768

如您所见,左边有750,这对我来说有点疲惫,我不知道原因。我可能会检查为什么它具有该值并更新此答案,但是现在我只是在motionLayout的onInterceptTouchEvent中删除了boundCheck,因为至少到目前为止我并没有真正使用特定区域。

更新:这是touchRegion和regionId。对于相同的场景和元素(比如说按钮),第一次单击它(在touchProcess中)可以正常工作,但是如果执行过渡,则它将在touchProcess中返回错误的视图,regionId返回另一个视图,它是上一个过渡场景的父级。我不知道为什么会发生这种情况,或者是某种错误(考虑到该库仍是beta版,可以理解)。因此,我只是重写了MotionLayout的touchProcess,而忽略了检查TouchRegion边界的部分。

© www.soinside.com 2019 - 2024. All rights reserved.