触摸时 ACTION_CANCEL

发布于 2024-11-07 08:41:18 字数 1220 浏览 0 评论 0原文

我有以下类,它代表一个可触摸的视图并绘制一个滑动条。

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

但是当触摸并拖动它时,我收到一个 ACTION_DOWN,一些 ACTION_MOVE 然后收到一个 ACTION_CANCEL 并且没有进一步的事件。

为什么会出现这样的情况呢?我不想取消该事件并使其继续拖动栏。

I has the following class that represents a View that is touchable and draw a Slide Bar.

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.

Why it's happens? I don't want to cancel the event and enable it to keep dragging bar.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

谢绝鈎搭 2024-11-14 08:41:18

当父容器拦截您的触摸事件时,就会发生这种情况。任何覆盖 ViewGroup.onInterceptTouchEvent(MotionEvent) 的 ViewGroup可以做到这一点(例如 ScrollView 或 ListView)。

处理此问题的正确方法是调用 ViewParent.requestDisallowInterceptTouchEvent(boolean) 一旦您认为需要保留运动事件,就在您的父视图上使用方法。

这是一个简单的示例(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
    }
}

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).

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.

Here's a quick example (attemptClaimDrag method is taken from android source code):

/**
 * 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
    }
}
停滞 2024-11-14 08:41:18

当父视图接管其子视图之一的控制权时,就会发生 ACTION_CANCEL

查看有关 ViewGroup 的文档。 onInterceptTouchEvent(MotionEvent) 方法。从链接:

  1. 您将在此处收到 down 事件。
  2. 按下事件将由该视图组的子视图组处理,或者交给您自己的 onTouchEvent() 方法来处理;这意味着您应该实现 onTouchEvent() 来返回 true,这样您将继续看到手势的其余部分(而不是寻找父视图来处理它)。此外,通过从 onTouchEvent() 返回 true,您将不会在 onInterceptTouchEvent() 中收到任何后续事件,并且所有触摸处理都必须在 onTouchEvent() 中进行代码> 像平常一样。
  3. 只要您从此函数返回 false,每个后续事件(直到并包括最终的 up)都将首先传递到此处,然后传递到目标的 onTouchEvent()
  4. 如果您从此处返回 true,您将不会收到任何以下事件:目标视图将收到相同的事件,但带有操作 ACTION_CANCEL,并且所有其他事件将传递到您的 onTouchEvent( ) 方法并且不再出现在这里

An ACTION_CANCEL happens when a parent view takes over control of one of its children views.

Take a look at the documentation around ViewGroup.onInterceptTouchEvent(MotionEvent) method. From the link:

  1. You will receive the down event here.
  2. The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; 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). 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.
  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().
  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
耶耶耶 2024-11-14 08:41:18

需要禁止父视图拦截触摸事件:

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
}

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
}
我的痛♀有谁懂 2024-11-14 08:41:18

触摸时Android ACTION_CANCEL

另一种替代方法是:
您有 ViewGroup ,其中包含 View

  1. ViewGroup 的方法 onInterceptTouchEvent() 对于 ACTION_DOWN 返回 false,对于 ACTION_DOWN 返回 true其他情况
  2. View 始终在 onTouchonTouchEvent 中返回 true
  3. 用户创建访客 -ACTION_DOWN< /代码>, ACTION_MOVE...

因此,您将看到下一个流程

  1. ACTION_DOWN迭代:

    ViewGroupdispatchTouchEvent>start< ev = ACTION_DOWN
    ViewGroup onInterceptTouchEvent false
        查看dispatchTouchEvent>start< ev = ACTION_DOWN
        查看 onTouch true
        查看dispatchTouchEvent>完成<真的
    ViewGroupdispatchTouchEvent>完成<真的
    
  2. ACTION_MOVE 迭代:

    ViewGroupdispatchTouchEvent>start< ev = ACTION_MOVE
    ViewGroup onInterceptTouchEvent true //<- 开始拦截
        查看dispatchTouchEvent>start< ev = ACTION_CANCEL //<- 通知视图控件被拦截
        查看 onTouch true
        查看dispatchTouchEvent>完成<真的
    ViewGroupdispatchTouchEvent>完成<真的
    
  3. ACTION_MOVE 迭代:

    ViewGroupdispatchTouchEvent>start< ev = ACTION_MOVE
    ViewGroupdispatchTouchEvent>完成<错误的
    

[Android 触摸事件]

Android ACTION_CANCEL while touching

Another alternative way it is:
You have ViewGroup with View inside.

  1. The ViewGroup's method onInterceptTouchEvent() return false for ACTION_DOWN and true for other cases
  2. The View always returns true in onTouch or onTouchEvent
  3. A user makes a guest -ACTION_DOWN, ACTION_MOVE...

As a result you will see the next flow

  1. ACTION_DOWN iteration:

    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:

    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:

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

[Android Touch event]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文