1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
N15.md 15 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Небольшие хитрости Flutter: быстрое понимание логики работы с жестами

Снова время для выпуска серии небольших советов. Сегодня мы поговорим о логике работы с жестами в Flutter. Давно я уже писал статью по анализу исходного кода "Глубинное понимание принципов касаний и свайпов", но недавно мне сказали, что анализ исходного кода трудноразрешим для понимания. Есть ли более простой и легко воспринимаемый способ? В этой статье мы рассмотрим работу с жестами в Flutter с более простого ракурса.

GestureDetector

Независимо от того используете вы InkWell, InkResponse, TextButton или ElevatedButton, все они используют логику обработки жестов из GestureDetector. Таким образом, чтобы начать работать с жестами в Flutter, следует начать с анализа GestureDetector.

В строгом смысле события жестов происходят из Listener, а GestureDetector представляет собой упаковку для Listener, чтобы избежать сложного анализа исходного кода. Можно просто представить себе это как то, что не все компоненты реагируют на жесты, только те, которые имеют Listener, что особенно важно при рекурсивной реакции на события касания.

Логика обработки событий в GestureDetector основана на реализации различных GestureRecognizer (распознавателей жестов). Различные распознаватели жестов реагируют на различные типы жестов, конкурируют друг с другом, и в конце концов один победитель выбирается в GestureArenaManager (арена).Кратко говоря, в арене жесты следуют за двумя основными правилами:

  • Каждый распознаватель может в любое время выбрать выход с поражением, когда он остаётся единственным в арене, он побеждает
  • Каждый распознаватель может объявить себя победителем в любой момент времени, тогда другие распознаватели больше не будут реагировать

Как показано на следующем рисунке, в GestureDetector существует 8 различных GestureRecognizer, которые обрабатывают различные ситуации. Они принимают участие в арене событий в зависимости от параметров, переданных при использовании GestureDetector.

image1.png

Пример: если вы используете 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? Как это показано ниже:

image5

Когда в области находится два GestureDetector, то при обычном нажатии из-за влияния времени отклика (deadline), событие onTap будет обработано только тогда, когда область закрывается (close). Однако различие состоит в том, что теперь внутри этой области могут находиться несколько Recognizer. В этом случае только первый Recognizer в списке может выиграть событие, как это показано выше для красной области размером 200x200 пикселей.

image6

Для случая нескольких 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.

image7

Тогда возникает вопрос: если в области клика есть два объекта TapGestureRecognizer, то при долгом нажатии, когда таймеры обоих объектов вызывают didExceedDeadline, будут ли они оба получать событие onTapDown?

image8Ответ: да, будут! При активации всех таймеров событие didExceedDeadline вызывается дважды, что приводит к отправке двух событий onTapDown. Однако последующее соперничество позволяет только одному контролю откликнуться на событие onLongPress.> Кроме того, если событие Down не было вызвано долгим нажатием, то это не приведёт к вызову двух событий onTapDown для двух GestureDetector.

Второе дополнение относится к Listener. Если вы хотите более подробно изучить реализацию GestureDetector, вы заметите, что его использование Listener отличается от ваших ожиданий. Использование Listener ограничивается событиями PointerDown, а событие onPointerUp не используется. Как же тогда GestureDetector реагирует на события Up и Move?

image9

Для ответа на этот вопрос обратимся к анализу исходного кода, представленному в статье "Глубокий взгляд на принципы работы с касаниями и свайпами" (ссылка). Но для простоты мы здесь представляем только заключение:

Только после получения события PointerDown соответствующий GestureRecognizer может быть добавлен в маршрутизатор событий PointerRouter и менеджер событий GestureArenaManager. А следующие события Up и Move обрабатываются через GestureBinding.

Проще говоря, только после получения события PointerDown GestureRecognizer сможет реагировать на последующие события других жестов, обрабатываемых унифицированно, а остальные события не требуют получения обратной связи через Listener.

ЗаключениеТак закончился наш небольшой совет, цель которого — помочь вам更好地理解Flutter中触摸处理的逻辑。如果您对更详细的实现感兴趣,建议阅读文章 "深入探讨触摸和滑动操作的工作原理"。如果有任何问题或意见,请在下方留言!

注意:原文档语言为中文,因此这里直接翻译了其中包含英文的部分,并保持整体格式一致。

正确答案应为: Так закончился наш небольшой совет, цель которого — помочь вам лучше понять логику обработки касаний в Flutter. Если вас интересует более детальная реализация, рекомендую прочесть статью "Deep dive into the principles of touch and swipe handling". Если у вас есть вопросы или замечания, пожалуйста, оставьте свои комментарии ниже!

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master