简体   繁体   English

MotionEvent.ACTION_CANCEL 在 onTouch 期间不会被触发

[英]MotionEvent.ACTION_CANCEL is not triggered during onTouch

I am having this problem where ACTION_CANCEL is not triggered, I have implemented it in my other project and it's working fine.我在没有触发ACTION_CANCEL情况下遇到了这个问题,我已经在我的另一个项目中实现了它并且它工作正常。 It seems that ACTION_UP is the only MotionEvent that is called after ACTION_DOWN .看来, ACTION_UP是唯一MotionEvent被称为后ACTION_DOWN I wanted to trigger ACTION_CANCEL once my finger is not anymore in the view or outside the screen.一旦我的手指不在视图中或屏幕外,我想触发ACTION_CANCEL

Sample scenario: I click on the view which is a LinearLayout btw, on ACTION_DOWN its background is changed to a "clicked/dimmed" version of the image and when ACTION_UP is triggered its background changes back to the default image only if the finger is within the LinearLayout.示例场景:我单击 LinearLayout 视图,在ACTION_DOWN它的背景更改为图像的“单击/变暗”版本,当触发ACTION_UP时,仅当手指在其中时,其背景才会更改回默认图像线性布局。 Now the problem is when I press it and kept my finger on the screen, and drag my finger outside the LinearLayout, ACTION_UP is still triggered where it shouldn't have.现在的问题是当我按下它并将我的手指放在屏幕上,然后将我的手指拖到 LinearLayout 之外时, ACTION_UP仍然在它不应该触发的地方被触发。

Here's my code:这是我的代码:

    dimView.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(final View view,
                final MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                Log.d("TAG", "DOWN");
                return true;
            } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                Log.d("TAG", "UP");
                return true;
            } else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
                Log.d("TAG", "CANCEL");
                return true;
            }
            return false;
        }
    });

where: dimView is a LinearLayout其中:dimView 是一个 LinearLayout

I've been debugging this for a really long time and it bothered me a lot since this came up very randomly. 我已经调试了这个很长一段时间了,因为这很随机地出现了很多困扰我。 I then tested my code on different devices and realized the API implementation changed with Android 4.2. 然后我在不同设备上测试了我的代码,并意识到Android实现了API实现的变化。 The expected behaviour and code work absolutely fine with Android 4.1.2 (tested on Galaxy Tab 2) but the bug you describe can be seen on a Nexus 7 (Android 4.2). Android 4.1.2(在Galaxy Tab 2上测试)的预期行为和代码工作绝对正常,但您描述的错误可以在Nexus 7(Android 4.2)上看到。 Apparently Android changed the way MotionEvents are handled in API 17. 显然,Android改变了在API 17中处理MotionEvent的方式。

One particular case when the bug does not occur is when the view is located in a GroupLayout under a ScrollView . 没有发生错误的一个特殊情况是视图位于ScrollView下的GroupLayout When scrolling is possible the ACTION_CANCEL gets fired. 当可以滚动时, ACTION_CANCEL会被触发。 However when no scrolling is possible the bug persists. 但是,如果无法滚动,则错误仍然存​​在。

At first I tried combining an OnClickListener and OnTouchListener so that the last can handle just the animations but to no avail. 起初我尝试组合OnClickListenerOnTouchListener以便最后一个只能处理动画,但无济于事。 Dispatching the Events from parents also doesn't work. 从父母调度事件也不起作用。

One workaround is to capture ACTION_MOVE events and check if the finger is located outside of the view's boundaries using v.getX() and v.getY() and comparing them to event.getX() and event.getY() respectably. 一种解决方法是捕获ACTION_MOVE事件并使用v.getX()v.getY()检查手指是否位于视图边界之外,并将它们与event.getX()event.getY()进行相应的比较。 A global boolean variable ( isOutside ) can be used to store the most current information. 全局布尔变量( isOutside )可用于存储最新信息。 Before firing up the ACTION_UP you can check the latest state of isOutside and perform your animations and action accordingly. 发射了之前ACTION_UP您可以检查的最新状态isOutside ,并相应地进行你的动画和动作。 You can also return true or false depending on whether you captured the event or not. 您还可以返回true或false,具体取决于您是否捕获了事件。

Update: After digging a bit here i found this solution: Android: Detect if user touches and drags out of button region? 更新:在这里挖了一下之后我发现了这个解决方案: Android:检测用户是否触摸并拖出按钮区域? and compiled this code. 并编译了这段代码。 The idea is the same except that it creates a rectangle and checks if the event boundaries are within the rectangle of the view. 这个想法是相同的,除了它创建一个矩形并检查事件边界是否在视图的矩形内。

    someView.setOnTouchListener(new View.OnTouchListener() {

                private Rect rect;

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.d(TAG,"Touched: "+event.getAction());
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        Log.d(TAG,"ACTION_DOWN");
                        animateImageButtonOnClick(v, event);
                        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                    }

                    if (event.getAction() == MotionEvent.ACTION_UP) {
                        Log.d(TAG,"ACTION_UP");
                        if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
                            Log.d(TAG,"ACTION_UP - outside");
                            animateImageButtonOnRelease(v, event);
                        }  else {
                            Log.d(TAG,"ACTION_UP - inside");
                            // do your stuff here  
                        }
                    }

                    if(event.getAction() == MotionEvent.ACTION_MOVE){
                        if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
                            animateImageButtonOnReleaseWithLowDuration(v, event);
                        }
                    }

                    if (event.getAction() == MotionEvent.ACTION_CANCEL){
                        Log.d(TAG,"ACTION_CANCEL");
                        animateImageButtonOnRelease(v, event);
                        return true;
                    }

                    return true;
                }
            });
Override dispatchTouchEvent method maybe deal with it;

dimView.setOnTouchListener(new OnTouchListener() { dimView.setOnTouchListener(new OnTouchListener(){

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            // to call super touch method
            return false;
        }
    });

@Override
public boolean dispatchTouchEvent(MotionEvent motionEvent) {
    // TODO Auto-generated method stub
    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        Log.d("TAG", "DOWN");
        return true;
    } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
        Log.d("TAG", "UP");
        return true;
    } else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
        Log.d("TAG", "CANCEL");
        return true;
    }
    return super.dispatchTouchEvent(motionEvent);
}

Solved it by using MotionEvent.ACTION_UP and MotionEvent.ACTION_CANCEL in a switch/when case:通过在 switch/when 情况下使用 MotionEvent.ACTION_UP 和 MotionEvent.ACTION_CANCEL 解决了这个问题:

when (event.action) {
    MotionEvent.ACTION_DOWN -> startBrushEvent()
    MotionEvent.ACTION_MOVE -> useBrush(image as ImageView, event)
    MotionEvent.ACTION_UP,
    MotionEvent.ACTION_CANCEL ->
        stopBrushEvent()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM