触摸时ACTION_CANCEL

问题描述 投票:0回答:4

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

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 并且没有进一步的事件。

为什么会发生这种情况?我不想取消该事件并使其继续拖动栏。

android events view touch
4个回答
62
投票

当父容器拦截您的触摸事件时,就会发生这种情况。任何覆盖 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
    }
}

32
投票

当父视图接管其子视图之一时,就会发生

ACTION_CANCEL

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

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

2
投票

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

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
}

1
投票

触摸时 Android ACTION_CANCEL

另一种替代方式是: 您有

ViewGroup
,里面有
View

  1. ViewGroup
    的方法
    onInterceptTouchEvent()
    对于
    false
    返回
    ACTION_DOWN
    ,对于其他情况返回
    true
  2. View
    始终在
    true
    onTouch
     中返回 
    onTouchEvent
  3. 用户成为访客 -
    ACTION_DOWN
    ACTION_MOVE
    ...

结果你会看到下一个流程

  1. 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
    迭代:

    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
    迭代:

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

[Android触摸事件]

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