java пример - Android:Разница между onInterceptTouchEvent и dispatchTouchEvent?




swipe activity (13)

Дополнительный ответ

Вот некоторые визуальные дополнения к другим ответам. Мой полный ответ here .

Метод dispatchTouchEvent() в ViewGroup использует onInterceptTouchEvent() чтобы выбрать, должен ли он немедленно обрабатывать событие touch (with onTouchEvent() ) или продолжать уведомлять методы dispatchTouchEvent() своих дочерних элементов.

В чем разница между onInterceptTouchEvent и dispatchTouchEvent в Android?

Согласно руководству разработчика Android, оба метода можно использовать для перехвата сенсорного события ( MotionEvent ), но в чем разница?

Как onInterceptTouchEvent , dispatchTouchEvent и onTouchEvent взаимодействуют вместе в иерархии представлений ( ViewGroup )?


public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}

Лучшим местом для демистификации является исходный код. Документы крайне неадекватны в объяснении этого.

dispatchTouchEvent фактически определен в Activity, View и ViewGroup. Подумайте об этом как о контроллере, который решает, как маршрутизировать события касания.

Например, самый простой случай - это View.dispatchTouchEvent, который будет маршрутизировать событие касания к OnTouchListener.onTouch, если он определен, или к методу расширения onTouchEvent .

Для ViewGroup.dispatchTouchEvent все сложнее. Необходимо выяснить, какое из его дочерних представлений должно получить событие (путем вызова child.dispatchTouchEvent). Это в основном алгоритм тестирования ударов, в котором вы определяете, какой ограничивающий прямоугольник дочернего представления содержит координаты точки касания.

Но прежде чем он сможет отправить событие в соответствующее дочернее представление, родитель может шпионить и / или перехватывать событие все вместе. Вот что такое onInterceptTouchEvent . Поэтому он сначала называет этот метод перед выполнением теста ударов, и если событие было захвачено (возвращая true из onInterceptTouchEvent), он отправляет ACTION_CANCEL в дочерние представления, чтобы они могли отказаться от обработки событий касания (от предыдущих событий касания) и с тех пор все события касания на родительском уровне отправляются в onTouchListener.onTouch (если определено) или onTouchEvent (). Также в этом случае onInterceptTouchEvent больше не вызывается.

Вы даже хотите переопределить [Activity | ViewGroup | View] .dispatchTouchEvent? Если вы не выполняете какую-либо настраиваемую маршрутизацию, вы, вероятно, не должны.

Основными методами расширения являются ViewGroup.onInterceptTouchEvent, если вы хотите отслеживать и / или перехватывать событие касания на родительском уровне и View.onTouchListener / View.onTouchEvent для обработки основных событий.

В целом его чрезмерно сложный дизайн imo, но android apis больше ориентирован на гибкость, чем простота.


Короткий ответ: dispatchTouchEvent() будет вызван в первую очередь.

Краткий совет: не следует переопределять dispatchTouchEvent() поскольку его трудно контролировать, иногда это может замедлить вашу производительность. ИМХО, я предлагаю переопределить onInterceptTouchEvent() .

Поскольку в большинстве ответов довольно четко сказано о событии касания потока в группе активности / просмотра / представлении, я только добавляю больше подробностей о коде этих методов в ViewGroup (игнорируя dispatchTouchEvent() ):

onInterceptTouchEvent() будет вызываться первым, событие ACTION будет вызываться соответственно вниз -> move -> up. Есть 2 случая:

  1. Если вы вернете false в 3 случаях (ACTION_DOWN, ACTION_MOVE, ACTION_UP), он будет считать, что родитель не будет нуждаться в этом событии касания , поэтому onTouch() родителей никогда не вызывает , но onTouch() вызывает onTouch() детей ; однако обратите внимание:

    • onInterceptTouchEvent() прежнему продолжает получать сенсорное событие, пока его дети не вызывают requestDisallowInterceptTouchEvent(true) .
    • Если нет детей, принимающих это событие (это может произойти в 2 случаях: нет детей в позиции, которую пользователи касаются, или есть дети, но она возвращает false в ACTION_DOWN), родители отправят это событие обратно в onTouch() родители.
  2. И наоборот, если вы вернетесь true , родитель немедленно украдет это событие касания , а onInterceptTouchEvent() немедленно остановится, вместо этого будет onTouch() родителей, а все onTouch() получат последнее действие - ACTION_CANCEL (значит, это означает, что родители украли событие касания, и дети с этого момента не могут справиться). Поток onInterceptTouchEvent() возвращает false, это нормально, но есть немного путаницы с возвратом true case, поэтому я перечисляю его здесь:

    • Верно true в ACTION_DOWN, onTouch() родителей снова получит ACTION_DOWN и последующие действия (ACTION_MOVE, ACTION_UP).
    • Верно true в ACTION_MOVE, onTouch() родителей получит следующий ACTION_MOVE (не тот же ACTION_MOVE в onInterceptTouchEvent() ) и следующие действия (ACTION_MOVE, ACTION_UP).
    • Вернуть true в ACTION_UP, onTouch() родителей НЕ будет вызываться вообще, потому что слишком поздно, чтобы родители украли событие касания.

Еще одна важная вещь: ACTION_DOWN события в onTouch() определяет, будет ли представление получать больше действий от этого события или нет. Если представление возвращает true в ACTION_DOWN в onTouch() , это означает, что представление желает получить больше действий от этого события. В противном случае return false в ACTION_DOWN в onTouch() будет означать, что представление не получит больше действий от этого события.


В Activity и View есть метод dispatchTouchEvent () и onTouchEvent.The ViewGroup тоже имеет эти методы, но имеет другой метод, называемый onInterceptTouchEvent. Тип возврата этих методов является логическим, вы можете управлять маршрутом отправки через возвращаемое значение.

Отправка события в Android начинается с Activity-> ViewGroup-> View.


dispatchTouchEvent обрабатывает до onInterceptTouchEvent.

Используя этот простой пример:

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

Вы можете видеть, что журнал будет выглядеть так:

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

Поэтому, если вы работаете с этими двумя обработчиками, используйте dispatchTouchEvent для обработки на первом экземпляре события, которое перейдет на onInterceptTouchEvent.

Другое отличие состоит в том, что если dispatchTouchEvent возвращает «false», событие не распространяется на дочерний элемент, в этом случае EditText, тогда как если вы вернете false в onInterceptTouchEvent, событие все равно получит отправку в EditText



Функция onInterceptTouchEvent() всегда является точкой входа для события ACTION_DOWN которое является первым событием.

Если вы хотите, чтобы ViewGroup обрабатывал этот жест, верните true из onInterceptTouchEvent() . При возврате true, onTouchEvent onTouchEvent() будет получать все последующие события до следующих ACTION_UP или ACTION_CANCEL , и в большинстве случаев события касания между ACTION_DOWN и ACTION_UP или ACTION_CANCEL являются ACTION_MOVE , которые обычно будут распознаваться как жесты прокрутки / прокрутки.

Если вы onInterceptTouchEvent() false из onInterceptTouchEvent() , будет вызван onInterceptTouchEvent() целевого представления. Он будет повторяться для последующих сообщений, пока вы не onInterceptTouchEvent() true из onInterceptTouchEvent() .

Источник: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html


Основное различие:

• Activity.dispatchTouchEvent (MotionEvent) - позволяет вашей активности перехватывать все события касания до их отправки в окно.
• ViewGroup.onInterceptTouchEvent (MotionEvent) - позволяет ViewGroup просматривать события по мере их отправки в дочерние представления.


Существует много путаницы в отношении этих методов, но на самом деле это не так сложно. Большая часть путаницы объясняется тем, что:

  1. Если ваша View/ViewGroup или любой из ее дочерних объектов не возвращает true в onTouchEvent , dispatchTouchEvent и onInterceptTouchEvent будут ТОЛЬКО вызываться для MotionEvent.ACTION_DOWN . Без true из onTouchEvent родительский взгляд будет предполагать, что вашему представлению не требуется MotionEvents.
  2. Когда ни один из дочерних элементов ViewGroup не возвращает true в onTouchEvent, onInterceptTouchEvent будет ТОЛЬКО вызываться для MotionEvent.ACTION_DOWN , даже если ваша ViewGroup возвращает true в onTouchEvent .

Порядок обработки выглядит следующим образом:

  1. вызывается dispatchTouchEvent .
  2. onInterceptTouchEvent вызывается для MotionEvent.ACTION_DOWN или когда любой из дочерних элементов ViewGroup возвращается true в onTouchEvent .
  3. onTouchEvent сначала вызывается дочерними элементами ViewGroup, и когда ни один из детей не возвращает true, он вызывается в View/ViewGroup .

Если вы хотите просмотреть TouchEvents/MotionEvents не отключая события от своих детей, вы должны сделать две вещи:

  1. Переопределить dispatchTouchEvent для предварительного просмотра события и возврата super.dispatchTouchEvent(ev) ;
  2. Переопределите onTouchEvent и верните true, иначе вы не получите ни одного MotionEvent кроме MotionEvent.ACTION_DOWN .

Если вы хотите обнаружить какой-то жест, например, событие салфетки, не отключая другие события для своих детей, пока вы не обнаруживаете жест, вы можете сделать это следующим образом:

  1. Просмотрите MotionEvents, как описано выше, и установите флаг, когда вы обнаружили свой жест.
  2. Возвращает true в onInterceptTouchEvent когда ваш флаг установлен для отмены обработки MotionEvent вашими детьми. Это также удобное место для сброса вашего флага, потому что onInterceptTouchEvent не будет вызываться снова до следующего MotionEvent.ACTION_DOWN .

Пример переопределений в FrameLayout (мой пример - это C #, поскольку я программирую с Xamarin Android, но логика в Java аналогична):

public override bool DispatchTouchEvent(MotionEvent e)
{
    // Preview the touch event to detect a swipe:
    switch (e.ActionMasked)
    {
        case MotionEventActions.Down:
            _processingSwipe = false;
            _touchStartPosition = e.RawX;
            break;
        case MotionEventActions.Move:
            if (!_processingSwipe)
            {
                float move = e.RawX - _touchStartPosition;
                if (move >= _swipeSize)
                {
                    _processingSwipe = true;
                    _cancelChildren = true;
                    ProcessSwipe();
                }
            }
            break;
    }
    return base.DispatchTouchEvent(e);
}

public override bool OnTouchEvent(MotionEvent e)
{
    // To make sure to receive touch events, tell parent we are handling them:
    return true;
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
    // Cancel all children when processing a swipe:
    if (_cancelChildren)
    {
        // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
        _cancelChildren = false;
        return true;
    }
    return false;
}

Потому что это первый результат в Google. Я хочу поделиться с вами замечательным Разговором Дэйвом Смитом на Youtube: Освоение системы Android Touch и слайды доступны here . Это дало мне хорошее глубокое понимание системы Android Touch:

Как работает Activity :

  • Activity.dispatchTouchEvent()
    • Всегда сначала называть
    • Отправляет событие в корневой вид, прикрепленный к окну
    • onTouchEvent()
      • Вызывается, если никакие представления не потребляют событие
      • Всегда называть

Как обрабатывается вид :

  • View.dispatchTouchEvent()
    • Сначала отправляет событие слушателю, если существует
      • View.OnTouchListener.onTouch()
    • Если не потребляется, обрабатывает сам сенсор
      • View.onTouchEvent()

Как ViewGroup обрабатывает касание:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • Проверьте, не должно ли это заменить детей
      • ACTION_CANCEL активному ребенку
      • Вернуть true один раз, потребляет все последующие события
    • Для каждого дочернего представления в обратном порядке они были добавлены
      • Если касание имеет значение (внутренний вид), child.dispatchTouchEvent()
      • Если предыдущее не обрабатывается, отправьте на следующий просмотр
    • Если дети не справляются с событиями, слушатель получает шанс
      • OnTouchListener.onTouch()
    • Если слушатель не прослушивается или не обрабатывается
      • onTouchEvent()
  • Перехваченные события перескакивают через дочерний шаг

Он также предоставляет пример кода пользовательского касания github.com/devunwired/ .

Ответ: В основном dispatchTouchEvent() вызывается на каждом уровне View чтобы определить, заинтересован ли View в продолжающемся жесте. В ViewGroup ViewGroup имеет возможность украсть события ViewGroup в ViewGroup dispatchTouchEvent() , прежде чем он вызовет dispatchTouchEvent() для детей. ViewGroup остановит только диспетчеризацию, если метод ViewGroup onInterceptTouchEvent() возвращает true. Разница заключается в том, что dispatchTouchEvent() отправляет MotionEvents а onInterceptTouchEvent сообщает, следует ли перехватывать (не отправлять MotionEvent детям) или нет (отправка детям) .

Вы можете представить, что код ViewGroup делает больше или меньше этого (очень упрощенного):

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

Малый ответ:

onInterceptTouchEvent приходит до setOnTouchListener.


Вот как вы это делаете в Mono для Android (AKA MonoDroid)

InputMethodManager imm = GetSystemService (Context.InputMethodService) as InputMethodManager;
if (imm != null)
    imm.HideSoftInputFromWindow (searchbox.WindowToken , 0);




java android event-handling event-listener android-touch-event