子项拦截触摸事件的MotionLayout问题 [英] MotionLayout problems with children intercepting touch events

查看:356
本文介绍了子项拦截触摸事件的MotionLayout问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在主布局中,我有一个用于根容器的motionLayout.在其中,还有其他视图.其中之一是frameLayout,其中包含一个片段.片段是一个页面,由NestedScrollView等组成.MotionLayout的OnSwipe仅适用于水平滑动,而NestedScrollView应该只能垂直滚动.我必须扩展MotionLayout的onInterceptTouchEvent方法(请看下面的代码),并且只将那些非水平的触摸事件传递给子代.到目前为止,它很简单,并且有效.

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.

问题是,当我开始滑动查看吸收某种触摸事件(例如按钮或NestedScrollView)的视图时,它会使motionLayout过渡混乱,并且会立即跳过帧,因此锚定与我的鼠标位置相对应.过渡几乎完全在一帧中进行,并且破坏了UI体验.我的猜测是,如果motionLayout使用X1和X2进行过渡,则X1值就是引起问题的值,它的值来自不应有的位置.但是,当然,当我开始滑动触摸式吸收元件时.

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.

这是我的customMotionLayout的OnInterceptTouchEvent的替代方法:

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
            }
        }
    }

这是相关的转换:

    <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和布局没有问题.

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.

也许如果我知道motionLayout转换与touchPositions有何关系,我可以解决它.

Maybe If I knew how motionLayout transitions are related to touchPositions, I could fix it.

更新1:我注意到,如果我单击一个空格(简单单击ACTION_DOWN),然后滑动,即使从那些顽皮的元素开始,问题也会有所不同.该单击将以某种方式更新过渡的协调位置.它以我单击的位置为起点,例如x1.然后,当我在吸收触摸事件的东西上滑动时,它从那里得到x2.现在,我在下一个帧中所看到的就像是从x1到x2滑动动作一样的结果.

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.

更新2:我注意到的另一件事与我的问题非常相关,那就是当我完成滑动后,过渡始终处于STARTED状态.就像我从x1滑动到x2一样,其状态从开始,完成然后开始.因此,我想我的下一次滑动出现时,它会认为我的手指一直处于触摸状态,因此我手动进行了吸吮操作,但是我没有这样做.当我完成一个完整的擦拭geture并完成它的操作,而屏幕上没有手指时,在听完转换并评估状态之后,日志中的结果如下所示.因此,最后一个被调用的事件开始了,这对我来说不合逻辑.

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

更新3:我注意到的另一件事是,当且仅当过渡成功更改了运动状态时,我们才会遇到这样的情况:通过单击这些触摸吸收元素之一,motionLayout不会接收到OnTouchEvent!(这是我正在处理的问题行为存在的情况),并且如果我在应用程序启动时不刷任何东西(还没有问题),请单击一个按钮,我在motionLayout中收到ACTION_DOWN和ACTINO_UP.因此,在过渡到新状态后,motionLayout中发生的任何事情,都会阻止motionLayout从子级接收OnTouchEvent.

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.

更新4:我对MotionLayout如何处理场景创建不是很熟悉,但是如果有人知道,那是因为MotionLayout动态地创建了一个场景,从而以某种方式阻止了onTouchEvent一直返回到父级?

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?

更新5:因此,当motionLayout不接收带有ACTION_DOWN的motionEvent时,就会出现问题.我看了看调用堆栈,发现它应该与视图吸收事件dispatchTouchEvent方法有关,当开始在触摸吸收元件上滑动时,为ACTION_DOWN返回true,而在其他情况下返回false.因此,在dispatchTouchEvent中返回true会导致motionLayout无法接收ACTION_DOWN并引起问题.在dispatchTouchEvent内部,用于区分两种情况的结果的代码是以下部分:

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;
        }
     }

例如,单击按钮时,OnTouchEvent(event)返回true,单击空格时返回false.

the OnTouchEvent(event) returns true when clicking on buttons for example and false when clicking on blank spaces.

我正在使用2.0.0-beta4版本的约束布局.

I'm using 2.0.0-beta4 version of Constraint Layout.

推荐答案

我偶然发现了一个类似的问题,在该问题中,我有一个主要的 RecyclerView 嵌套了 RecyclerView s它.

I stumbled upon a similar issue, where I had a main RecyclerView that had nested RecyclerViews in it.

在寻找解决方案时,我发现了一种名为

While searching for a solution, I found a method called setInteractionEnabled(boolean enabled) inside the MotionLayout class. Calling that method with false value resolved my issue.

默认情况下,看起来 MotionLayout onTouchEvent onInterceptTouchEvent 之类的方法中执行一些逻辑.方法 setInteractionEnabled 基本上允许我们指定是否需要该逻辑.

It looks like that MotionLayout by default performs some logic inside such methods as onTouchEvent and onInterceptTouchEvent. The method setInteractionEnabled basically allows us to specify if we need that logic or not.

这篇关于子项拦截触摸事件的MotionLayout问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆