Снова время для выпуска серии небольших советов. Сегодня мы поговорим о логике работы с жестами в Flutter. Давно я уже писал статью по анализу исходного кода "Глубинное понимание принципов касаний и свайпов", но недавно мне сказали, что анализ исходного кода трудноразрешим для понимания. Есть ли более простой и легко воспринимаемый способ? В этой статье мы рассмотрим работу с жестами в Flutter с более простого ракурса.
Независимо от того используете вы InkWell
, InkResponse
, TextButton
или ElevatedButton
, все они используют логику обработки жестов из GestureDetector
. Таким образом, чтобы начать работать с жестами в Flutter, следует начать с анализа GestureDetector
.
В строгом смысле события жестов происходят из
Listener
, аGestureDetector
представляет собой упаковку дляListener
, чтобы избежать сложного анализа исходного кода. Можно просто представить себе это как то, что не все компоненты реагируют на жесты, только те, которые имеютListener
, что особенно важно при рекурсивной реакции на события касания.
Логика обработки событий в GestureDetector
основана на реализации различных GestureRecognizer
(распознавателей жестов). Различные распознаватели жестов реагируют на различные типы жестов, конкурируют друг с другом, и в конце концов один победитель выбирается в GestureArenaManager
(арена).Кратко говоря, в арене жесты следуют за двумя основными правилами:
Как показано на следующем рисунке, в GestureDetector
существует 8 различных GestureRecognizer
, которые обрабатывают различные ситуации. Они принимают участие в арене событий в зависимости от параметров, переданных при использовании GestureDetector
.
Пример: если вы используете GestureDetector
и настраиваете его на onTap
, onLongPress
и onDoubleTap
, как они обрабатывают эти события?
Основная идея здесь заключается в том, что решение зависит от времени (deadline
). Как onLongPress
, так и onDoubleTap
используют время для определения победителя. Например, когда пользователь просто нажимает, как показано ниже:
Поскольку по умолчанию значение deadline
для LongPressGestureRecognizer
составляет 500 миллисекунд, до того момента, как таймер достигнет значения 500 мс, событие PointerUpEvent
приведёт к завершению таймера долгого нажатия, и событие долгого нажатия не будет сработано.
С другой стороны, если событие PointerUpEvent
не произойдёт, то через 500 мс LongPressGestureRecognizer
отреагирует, объявив победу (акцепт).Аналогичная логика используется в DoubleTapGestureRecognizer
. Дeadline для двойного нажатия составляет 300 миллисекунд. При первом нажатии пользователя регистрируется таймер. Если во время этих 300 мс нет второго нажатия, DoubleTapGestureRecognizer
объявит "поражение" и прекратит работу, а если же за эти 300 мс происходит второе нажатие, то он объявит "победу", вызвав двойное нажатие.
Тогда кто-то может спросить: "Почему при двойном нажатии не срабатывает одиночное нажатие (onTap
)"? Для этого стоит обратиться к логике работы TapGestureRecognizer
.
Продолжая пример с GestureDetector
, который был настроен для onTap
, onLongPress
и onDoubleTap
, можно сказать, что при обычном нажатии пользователя:
LongPressGestureRecognizer
ещё не достиг значения 500 мс и завершается из-за события PointerUpEvent
DoubleTapGestureRecognizer
завершается из-за истечения времени таймера (300 мс) без следующего нажатияТаким образом, когда оба события — долгое нажатие и двойное нажатие — завершаются, в GestureArenaManager
остаётся только TapGestureRecognizer
. В этом случае GestureArenaManager
закрывается, запускается логика очистки (sweep
) и последний оставшийся Recognizer
объявляет победу, вызывая событие onTap
.
Поэтому TapGestureRecognizer
выигрывает благодаря тому, что остается последним.
На основе этого примера, совмещая его с ранее описанными логиками, можно легко понять принцип работы конкурса жестов в Flutter и роль ключевых значений deadline
.# Несколько GestureDetector
Ранее рассматривались сценарии, где использовался только один GestureDetector
. Но что происходит, если используется два GestureDetector
? Как это показано ниже:
Когда в области находится два GestureDetector
, то при обычном нажатии из-за влияния времени отклика (deadline
), событие onTap
будет обработано только тогда, когда область закрывается (close
). Однако различие состоит в том, что теперь внутри этой области могут находиться несколько Recognizer
. В этом случае только первый Recognizer
в списке может выиграть событие, как это показано выше для красной области размером 200x200 пикселей.
Для случая нескольких GestureDetector
порядок Recognizer
в списке участников (List<GestureArenaMember>
) зависит от рекурсивного вызова метода HitTest
. Проще говоря, рекурсивный вызов позволяет получить список HitTestResult
сверху вниз, а последний потомок (child
) окажется на вершине этого списка.
При наличии одного GestureDetector
, TapGestureRecognizer
является первым в списке _recognizers
, поэтому first
будет реагировать на TapGestureRecognizer
. Подробнее можно прочитать в статье «Глубокое понимание принципов работы сенсорных и скользящих движений».
Проще говоря:- При расположении двух GestureDetector
в списке участников (members
), красный GestureDetector
, являющийся потомком, благодаря рекурсивному вызову HitTest
, будет располагаться в начале списка.
Внутри каждого GestureDetector
TapGestureRecognizer
будет первым в списке _recognizers
.Итак, member.first
в конечном итоге реагирует на TapGestureRecognizer
. Исходя из двух основных законов, если объединить эти законы со сценариями использования нескольких GestureDetector
, получаем следующее:
Каждый Recognizer
может в любое время выбрать отказаться от события; он выигрывает, когда остаётся единственным участником в конкурсе. Если же других участников больше, то при закрытии конкурса (close
) member.first
получает право на реакцию.
Каждый Recognizer
может объявить себя победителем в любой момент, после чего все остальные Recognizer
прекращают свою активность.
Ранее были рассмотрены базовые принципы обработки жестов в Flutter. Здесь будут представлены ещё две важные точки:
Сначала, когда пользователь выполняет долгое нажатие, когда GestureDetector
отправляет событие onTapDown
?
(Продолжение следует...) Это связано с другим параметром deadline
, при долгом нажатии Recognizer
запускает другой таймер, который затем вызывает didExceedDeadline
для отправки события onTapDown
.
Тогда возникает вопрос: если в области клика есть два объекта TapGestureRecognizer
, то при долгом нажатии, когда таймеры обоих объектов вызывают didExceedDeadline
, будут ли они оба получать событие onTapDown
?
Ответ: да, будут! При активации всех таймеров событие didExceedDeadline вызывается дважды, что приводит к отправке двух событий onTapDown. Однако последующее соперничество позволяет только одному контролю откликнуться на событие onLongPress.> Кроме того, если событие
Down
не было вызвано долгим нажатием, то это не приведёт к вызову двух событий onTapDown
для двух GestureDetector
.
Второе дополнение относится к Listener
. Если вы хотите более подробно изучить реализацию GestureDetector
, вы заметите, что его использование Listener
отличается от ваших ожиданий. Использование Listener
ограничивается событиями PointerDown
, а событие onPointerUp
не используется. Как же тогда GestureDetector
реагирует на события Up
и Move
?
Для ответа на этот вопрос обратимся к анализу исходного кода, представленному в статье "Глубокий взгляд на принципы работы с касаниями и свайпами" (ссылка). Но для простоты мы здесь представляем только заключение:
Только после получения события
PointerDown
соответствующийGestureRecognizer
может быть добавлен в маршрутизатор событийPointerRouter
и менеджер событийGestureArenaManager
. А следующие событияUp
иMove
обрабатываются черезGestureBinding
.
Проще говоря, только после получения события PointerDown
GestureRecognizer
сможет реагировать на последующие события других жестов, обрабатываемых унифицированно, а остальные события не требуют получения обратной связи через Listener
.
注意:原文档语言为中文,因此这里直接翻译了其中包含英文的部分,并保持整体格式一致。
正确答案应为: Так закончился наш небольшой совет, цель которого — помочь вам лучше понять логику обработки касаний в Flutter. Если вас интересует более детальная реализация, рекомендую прочесть статью "Deep dive into the principles of touch and swipe handling". Если у вас есть вопросы или замечания, пожалуйста, оставьте свои комментарии ниже!
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )