【问题标题】:ACTION_CANCEL while touching触摸时 ACTION_CANCEL
【发布时间】:2011-08-26 11:23:04
【问题描述】:

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

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


    【解决方案1】:

    这将在父容器拦截您的触摸事件时发生。任何覆盖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
        }
    }
    

    【讨论】:

    • 当我有一个带有可滑动内容的 ViewPager 时,这对我有用。内容没有收到水平滑动,因为它们被 ViewPager 拦截。我只是将 ViewPager 的引用传递给子视图,子视图现在为所有 ACTION_DOWN 事件调用 mParent.requestDisallowInterceptTouchEvent(true)。
    • 如果您使用 ViewPager,您可能应该使用 ViewPager#canScroll(android.view.View, boolean, int, int, int) 代替。 ViewPager 的这种受保护方法专门用于确定 ViewPager 何时应滚动页面内容或切换到下一页。
    • 我如何请求DisallowInterceptTouchEvent(true);
    • 哇,我浪费了很多时间来解决这个问题。谢谢你们。 mParent.requestDisallowInterceptTouchEvent(true);成功了,我不必做 mParent.requestDisallowInterceptTouchEvent(false);在 up 事件上。它自己处理。无论我做什么,我的浏览器总是在滚动。
    • 调用 mParent.requestDisallowInterceptTouchEvent(true);在 onTouchEvent 里面。外面[例如。在片段级别]它不起作用。
    【解决方案2】:

    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() 方法并且不再出现在这里

    【讨论】:

    • 我意识到只有在将这个组件添加到 PopupWindow 时才会发生这种情况,我怎样才能拦截事件并将它们全部传递给它的孩子?
    • 我意识到这是一个 Horizo​​ntalScrollView 正在抓取我的事件。
    【解决方案3】:

    另一种方法是: 你有ViewGroup,里面有View

    1. ViewGroup 的方法 onInterceptTouchEvent() 返回 false 用于 ACTION_DOWNtrue 用于其他情况
    2. View 总是在onTouchonTouchEvent 中返回true
    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
      

    [Touch event flow]

    【讨论】:

      猜你喜欢
      • 2013-10-30
      • 1970-01-01
      • 2022-06-19
      • 1970-01-01
      • 1970-01-01
      • 2012-11-03
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      相关资源
      最近更新 更多