Подтвердить что ты не робот

ACTION_CANCEL при касании

У меня есть следующий класс, представляющий View, который можно прикоснуться и нарисовать панель слайдов.

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_MOVEs получают ACTION_CANCEL и никаких дальнейших событий.

Почему это происходит? Я не хочу отменять событие и позволять ему удерживать панель перетаскивания.

4b9b3361

Ответ 1

ACTION_CANCEL происходит, когда родительское представление берет на себя управление одним из своих дочерних представлений.

Ознакомьтесь с документацией по методу ViewGroup.onInterceptTouchEvent(MotionEvent). По ссылке:

  1. Вы получите событие внизу здесь.
  2. Событие down будет обработано либо дочерним элементом этой группы представлений, либо передано вашему собственному onTouchEvent() для обработки; это означает, что вы должны реализовать onTouchEvent() для возврата true, поэтому вы будете продолжать видеть остальную часть жеста (вместо того, чтобы искать родительское представление для его обработки). Кроме того, возвращая true из onTouchEvent(), вы не получите никаких следующих событий в onInterceptTouchEvent() и вся обработка касания должна происходить в onTouchEvent() как обычно.
  3. До тех пор, пока вы возвращаете false из этой функции, каждое последующее событие (вплоть до финала и включая его) будет доставляться сначала сюда, а затем к цели onTouchEvent().
  4. Если вы вернете истину отсюда, вы не получите никаких следующих событий: целевое представление получит то же событие, но с действием ACTION_CANCEL, и все дальнейшие события будут доставлены в ваш onTouchEvent() и больше не будут отображаться здесь.

Ответ 2

Это произойдет, когда родительский контейнер перехватит ваше событие касания. Любая ViewGroup, которая переопределяет ViewGroup.onInterceptTouchEvent(MotionEvent), может сделать это (например, ScrollView или ListView).

Правильный способ справиться с этим - вызвать метод ViewParent.requestDisallowInterceptTouchEvent(boolean) в своем родительском представлении, если вы считаете, что вам нужно сохранить событие движения.

Вот быстрый пример (метод tryClaimDrag взят из исходного кода android):

/**
 * Tries to claim the user 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
    }
}

Ответ 3

Еще один альтернативный способ это: У вас есть 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
    

onTouch поток