Разрешить родительскому представлению принимать MotionEvents, если дочерний элемент возвращает false

У меня есть приложение, которому требуется обработка событий необычным способом.

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

Предположим, что у меня есть FrameLayout (с этого момента я буду называть его ViewSwiper), что все представления, добавленные к нему, являются MATCH_PARENT X MATCH_PARENT (которое я буду называть PageView), он обрабатывает события, переводя фактическое представление и заменяя его в зависимости от направления перемещения. Этот компонент я уже сделал и работает правильно ( Использование анимации для просмотра просмотров ).

Но проблема заключается в том, что PageView я добавляю поверх него, в случае ImageViews, которые возвращают false в своем onTouchEvent, ViewSwiper будет обрабатывать события и позволять другому PageView выходить на экран, но если я добавлю ScrollView, все события будут потребляться прокруткой, и у ViewSwiper не будет возможности заменить PageView.

Итак, я понял, что возвращая false onTouchEvent ScrollView, родитель может предположить, что это события, я написал этот подкласс ScrollView, чтобы проверить его:

public class ScrollViewVertical extends ScrollView {
    public ScrollViewVertical(Context context) {
        super(context);
        setOverScrollMode(OVER_SCROLL_ALWAYS);
        setVerticalScrollBarEnabled(false);
    }

    public boolean onTouchEvent(MotionEvent evt) {
        super.onTouchEvent(evt);
        return false;
    }
}

Но возврат false приводит к тому, что любые дальнейшие события отправляются родителю, но мне нужны эти события для ВЕРТИКАЛЬНОЙ прокрутки, поэтому у меня есть идея возвращать false только в том случае, если пользователь перемещается по ГОРИЗОНТАЛЬНОМУ, вот как выглядит мой код:

public class ScrollViewVertical extends ScrollView {
    private MovementTracker moveTracker;
    public ScrollViewVertical(Context context) {
        super(context);
        setOverScrollMode(OVER_SCROLL_ALWAYS);
        setVerticalScrollBarEnabled(false);
        moveTracker = new MovementTracker();
    }
    public boolean onTouchEvent(MotionEvent evt) {
        if (moveTracker.track(evt))
            if (moveTracker.getDirection() == Direction.HORIZONTAL)
                return false;

        return super.onTouchEvent(evt);
    }
}

PS: MovementTracker вернет true в track() после некоторых событий и сообщит, в каком направлении движется пользователь.

Но в этом случае ScrollView продолжает получать события, поскольку возвращает true для первых событий.

Любые идеи о том, как я могу обрабатывать события в ViewSwiper, когда его дочерний элемент возвращает false (даже если возвращаются некоторые истинные значения).

PS: я могу дать больше информации об этом, если это необходимо, а также принять различные решения.

Основываясь на ответах, я попробовал следующее:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    onTouchEvent(ev);
    return intercept;
}

public boolean onTouchEvent(MotionEvent evt) {
    boolean x = super.onTouchEvent(evt);

    if (moveTracker.track(evt)) {
        intercept = moveTracker.getDirection() != Direction.VERTICAL;
        if (!intercept)
            getParent().requestDisallowInterceptTouchEvent(false);
    }

    return x;
}

Еще ничего.


person Marcos Vasconcelos    schedule 06.09.2011    source источник


Ответы (2)


попробуйте это в onTouchEvent() прокрутки

     //if (evt.getAction() == MotionEvent.ACTION_MOVE) {
    if (moveTracker.track(evt)){
       if (moveTracker.getDirection() == Direction.VERTICAL){
          //Or the direction you want the scrollview keep moving
          getParent().requestDisallowInterceptTouchEvent(true);

            }
        } 
 return true;

Обновлять

Пожалуйста, попробуйте следующее для пользовательского Scrollview

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
    return false;
}

И ничего больше

Таким образом, я предполагаю, что MotionEvent будет работать в обоих представлениях. И поскольку они не конфликтуют (один вертикальный, другой горизонтальный), это может сработать.

person weakwire    schedule 06.09.2011
comment
Это ничего не делает, один вопрос, в этом случае я также должен реализовать onInterceptTouchEvent? И если это так, как я узнаю, попросил ли ребенок его перехватить? - person Marcos Vasconcelos; 06.09.2011
comment
Попробуйте всегда возвращать true. Техника здесь заключается в том, что вы хотите, чтобы по умолчанию все сенсорные события переходили к parent.getParent(). Это должно работать, если все события (даже в прокрутке) контролируются родительским представлением до тех пор, пока ребенок не перехватит их, а не наоборот. - person weakwire; 06.09.2011
comment
Итак, как мне сделать так, чтобы родитель обрабатывал все события, а дочерний — перехватывал? - person Marcos Vasconcelos; 06.09.2011
comment
это идея да. Обработка всех событий MOVE. Не другие. Таким образом, только ребенок может перехватить - person weakwire; 07.09.2011
comment
Для этого переопределите dispatchTouchEvent() в своих дочерних элементах и ​​не вызывайте super.dispatchTouchEvent(). Через groups.google.com/group/android-developers/browse_thread/thread / - person weakwire; 07.09.2011
comment
Я попробовал это решение, события перестают поступать в ScrollView, но ViewSwiper onTouch не вызывался. - person Marcos Vasconcelos; 07.09.2011
comment
Все еще ничего, я сделал много попыток с этим методом, но все равно ничего, если событие перестает обрабатываться в этом ScrollView, родитель все равно не предполагает его. - person Marcos Vasconcelos; 08.09.2011

Основываясь на ответе от weakwire, я пришел к следующему решению:

На ViewSwiper

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(!super.dispatchTouchEvent(ev))
            onTouchEvent(ev);
        return true;
    }

И в ScrollHorizontal я возвращаю false в dispatchTouchEvent, когда мне это больше не нужно.

person Marcos Vasconcelos    schedule 08.09.2011