简体   繁体   English

触摸时 ACTION_CANCEL

[英]ACTION_CANCEL while touching

I has the following class that represents a View that is touchable and draw a Slide Bar.我有以下 class 表示可触摸的视图并绘制滑动条。

public class SlideBar extends View {
private int progress;
private int max;

private Paint background;
private Paint upground;

private RectF bar;

private boolean firstDraw;

public SlideBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    progress = 0;

    upground = new Paint();
    upground.setColor(Color.parseColor("#C2296C"));

    background = new Paint();
    background.setColor(Color.parseColor("#777777"));
}

private void onFirstDraw() {
    max = getWidth();
    bar = new RectF(0, 19, max, 21);
}

public void onDraw(Canvas canvas) {
    if (!firstDraw) {
        onFirstDraw();
        progress = max;
        firstDraw = true;
    }

    canvas.save();
    canvas.drawRoundRect(bar, 5, 5, background);
    canvas.drawCircle(progress, 20, 9, upground);
    canvas.restore();
}

public void setValue(int value) {
    progress = value;
}

public boolean onTouchEvent(MotionEvent evt) {
    System.out.println(evt.getAction());
    progress = (int) evt.getX();
    invalidate();
    return false;
}
}

But when touching and dragging it, I receive a ACTION_DOWN, some ACTION_MOVEs then receive a ACTION_CANCEL and no further events.但是当触摸和拖动它时,我收到一个 ACTION_DOWN,然后一些 ACTION_MOVE 收到一个 ACTION_CANCEL 并且没有进一步的事件。

Why it's happens?为什么会这样? I don't want to cancel the event and enable it to keep dragging bar.我不想取消事件并使其继续拖动栏。

This will happen when parent container will intercept your touch event.当父容器拦截您的触摸事件时,就会发生这种情况。 Any ViewGroup that overrides ViewGroup.onInterceptTouchEvent(MotionEvent) can do that (ScrollView or ListView for instance).任何覆盖ViewGroup.onInterceptTouchEvent(MotionEvent) 的 ViewGroup都可以做到这一点(例如 ScrollView 或 ListView)。

Proper way to deal with this is to call ViewParent.requestDisallowInterceptTouchEvent(boolean) method on your parent view once you think you need to keep the motion event.处理此问题的正确方法是在您认为需要保留运动事件时在父视图上调用ViewParent.requestDisallowInterceptTouchEvent(boolean)方法。

Here's a quick example (attemptClaimDrag method is taken from android source code):这是一个快速示例(attemptClaimDrag 方法取自 android 源代码):

/**
 * Tries to claim the user's drag motion, and requests disallowing any
 * ancestors from stealing events in the drag.
 */
private void attemptClaimDrag() {
    //mParent = getParent();
    if (mParent != null) {
        mParent.requestDisallowInterceptTouchEvent(true);
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        if (iWantToKeepThisEventForMyself(event)) {
            attemptClaimDrag();
        }
        //your logic here
    } else {
        //your logic here
    }
}

An ACTION_CANCEL happens when a parent view takes over control of one of its children views.当父视图接管其子视图之一的控制时,会发生ACTION_CANCEL

Take a look at the documentation around ViewGroup.onInterceptTouchEvent(MotionEvent) method.查看有关ViewGroup.onInterceptTouchEvent(MotionEvent)方法的文档。 From the link:从链接:

  1. You will receive the down event here.您将在此处收到 down 事件。
  2. The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; down 事件要么由这个视图组的子视图处理,要么交给你自己的onTouchEvent()方法来处理; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it).这意味着您应该实现onTouchEvent()以返回 true,因此您将继续看到手势的其余部分(而不是寻找父视图来处理它)。 Also, by returning true from onTouchEvent() , you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.此外,通过从onTouchEvent()返回 true,您将不会在onInterceptTouchEvent()收到任何后续事件,并且所有触摸处理都必须像往常一样在onTouchEvent()
  3. For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent() .只要您从此函数返回 false,每个后续事件(直到并包括最终事件)将首先在此处传递,然后再传递到目标的onTouchEvent()
  4. If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL , and all further events will be delivered to your onTouchEvent() method and no longer appear here如果您从这里返回 true,您将不会收到任何以下事件:目标视图将收到相同的事件,但带有ACTION_CANCEL操作,并且所有进一步的事件将被传递到您的onTouchEvent()方法并且不再出现在此处

Need to disallow parent view to intercept the touch event:需要禁止父视图拦截触摸事件:

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            (parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(true)
        }
        MotionEvent.ACTION_UP -> {
            (parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(false)
        }
        else -> {
        }
    }
    return true
}

Another alternative way it is: You have ViewGroup with View inside.另一种替代方法是:您有ViewGroupView里面。

  1. The ViewGroup 's method onInterceptTouchEvent() return false for ACTION_DOWN and true for other casesViewGroup的方法onInterceptTouchEvent()返回falseACTION_DOWNtrue对于其它情况
  2. The View always returns true in onTouch or onTouchEvent ViewonTouchonTouchEvent总是返回true
  3. A user makes a guest - ACTION_DOWN , ACTION_MOVE ...用户成为访客 - ACTION_DOWN , ACTION_MOVE ...

As a result you will see the next flow结果你会看到下一个流程

  1. ACTION_DOWN iteration: ACTION_DOWN迭代:

     ViewGroup dispatchTouchEvent >start< ev = ACTION_DOWN ViewGroup onInterceptTouchEvent false View dispatchTouchEvent >start< ev = ACTION_DOWN View onTouch true View dispatchTouchEvent >finish< true ViewGroup dispatchTouchEvent >finish< true
  2. ACTION_MOVE iteration: ACTION_MOVE迭代:

     ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE ViewGroup onInterceptTouchEvent true //<- start intercepting View dispatchTouchEvent >start< ev = ACTION_CANCEL //<- View is notified that control was intercepted View onTouch true View dispatchTouchEvent >finish< true ViewGroup dispatchTouchEvent >finish< true
  3. ACTION_MOVE iteration: ACTION_MOVE迭代:

     ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE ViewGroup dispatchTouchEvent >finish< false

[Touch event flow] 【触摸事件流程】

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

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