[Перевод] Подробные жесты флаттера

внешний интерфейс контейнер Программа перевода самородков Flutter

Flutter предоставляет несколько отличных готовых компонентов для обработки сенсорных событий, таких какin InkWellиInkResponse. Оберните свои компоненты этими компонентами, и они будут реагировать на события касания. Среди прочего, он добавит всплески в стиле Material к вашим компонентам. Например, при расширении за пределы компонентаInkResponseПри желании управляйте формой и обрезанием всплеска. интересноInkWellиInkResponseне будет выполнять никакого рендеринга, но обновит родительскийMaterialкомпоненты. Типичный пример — фотографии. Если вы используетеinkEllОберните изображение, и вы заметите, что рябь не видна. Это потому, что он рисуется за изображением на материале. хочу сделатьInkЭффект всплеска виден и может использоваться сInk.ImageОберните картинку. Хотя это полезно для большинства задач, если вы хотите зафиксировать больше событий, например, когда пользователь перетаскивает экран, вы должны использоватьGestureDetector.

Так что же такое детектор жестов? Как это работает?

Проще говоря, детектор жестов — это компонент без состояния, параметры которого в его конструкторе могут использоваться для различных событий касания. Стоит отметить, что вы не можете использовать обаPanиScale,так какScaleдаPanнадмножество .GestureDetectorчисто для обнаружения жестов, поэтому никакого визуального ответа не дается (нетMaterial Inkраспространять).

Ниже представлена ​​таблица, показывающаяGestureDetectorРазличные предоставленные обратные вызовы и их соответствующие краткие описания:

свойство/обратный вызов описывать
onTapDown Срабатывает каждый раз, когда пользователь касается экранаOnTapDown.
onTapUp Когда пользователь перестает касаться экрана,onTapUpназывается.
onTap При кратковременном касании экранаonTapсрабатывает.
onTapCancel Когда пользователь касается экрана, не закончивTap, это событие будет запущено.
onDoubleTap Вызывается при двойном касании экрана в быстрой последовательностиonDoubleTap.
onLongPress Пользователь касается экрана больше, чем500 мсчас,onLongPressсрабатывает.
onVerticalDragDown Когда указатель соприкасается с экраном и начинает двигаться вертикально,onVerticalDownназывается.
onVerticalDragStart когда указательНачинатьВызывается при движении в вертикальном направленииonVerticalDragStart.
onVerticalDragUpdate Этот метод вызывается каждый раз при изменении положения указателя на экране.
onVerticalDragEnd Когда пользователь перестает двигаться, перетаскивание считается завершенным и вызывается это событие.
onVerticalDragCancel Вызывается, когда пользователь внезапно прекращает перетаскивание.
onHorizontalDragDown Вызывается, когда пользователь/указатель вступает в контакт с экраном и начинает двигаться горизонтально.
onHorizontalDragStart Пользователь/указатель коснулся экрана иНачинатьДвигайтесь горизонтально.
onHorizontalDragUpdate Вызывается каждый раз, когда положение указателя по горизонтали/оси x изменяется.
onHorizontalDragEnd Это событие вызывается, когда заканчивается горизонтальное перетаскивание.
onHorizontalDragCancel Когда указатель не срабатывает успешноonHorizontalDragDownкогда звонили.
onPanDown Вызывается, когда указатель касается экрана.
onPanStart Когда событие указателя начинает двигаться,onPanStartкурок.
onPanUpdate Каждый раз, когда указатель меняет положение, вызовитеonPanUpdate.
onPanEnd Это событие вызывается, когда панорамирование завершено.
onScaleStart Это событие вызывается, когда указатель касается экрана и устанавливает фокус на 1.0.
onScaleUpdate Указатель, касающийся экрана, указывает на новый фокус.
onScaleEnd Вызывается, когда указатель больше не соприкасается с экраном, указывая на завершение жеста.

GestureDetectorКакие жесты пытаться распознать, определяются на основе того, какой обратный вызов не является нулевым. Это полезно, потому что если вам нужно отключить жест, вам нужно передатьnull.

Давайте начнем с **onTap** Жесты в качестве примера определяют, как обращаться с **GestureDetector**.

Во-первых, мы используемonTapОбратный вызов создает GestureDetector, поскольку он не равен нулю, когда происходит событие касания.GestureDetectorбудет использовать наш обратный вызов. существуетGestureDetectorВнутри создаетGesture Factory.Gesture RecognizerПроводится большая работа, чтобы определить, какой жест обрабатывается. Этот процесс предназначен дляGestureDetectorТо же самое для всех предоставленных обратных вызовов.GestureFactoriesзатем будет переданоRawGestureDetector.

RawGestureDetectorПридется проделать большую работу по обнаружению жестов. этокомпоненты с состоянием, синхронизирует все жесты при изменении состояния, обрабатывает распознаватели, получает все событиясобытия указателяи отправьте его зарегистрированному распознавателю. тогда они будутЖест АренаСхватка между самцами и самками.

RawGestureDetectorbuildМетод сборки состоит из базового класса для прослушивания событий указателя.Listenerсочинение. Это ваш предпочтительный класс, если вы хотите использовать необработанные входные данные с платформы, такие как события вверх, вниз или отмена.Listenerне дает вам никаких жестов, только основныеonPointerDown,onPointerUp,onPointerMoveиonPointerCancelсобытие. Все должно обрабатываться вручную, в том числеЖест Аренасообщить о себе. Если вы этого не сделаете, вы не получите автоматическую отмену и не сможете участвовать во взаимодействиях, которые там происходят. Этосторона компонентанижний слой.

ListenerЯвляетсяSingleChildRenderObjectWidget, унаследовано отRenderProxyBoxWithHitTestBehaviorтипRenderPointerListenerсоставной, что означает, что он имитирует свойства своих подклассов, позволяя при этом настраиватьHitTestBehavior. Если вы хотите узнать больше о блоках рендеринга и о том, как они работают, прочитайтеNorbert Kozsirнаписал эту статью.

HitTestBehaviourЕсть три варианта,deferToChild,opaqueиtranslucent. это изGestureDetector, и там можно настроить.DeferToChildПередавать события вниз по дереву компонентов, что такжеПоведение по умолчанию.Opaqueпредотвратит получение фоновыми компонентами событий, в то время какTranslucentЭто позволяет фоновым компонентам получать события.

Так что, если вы хотите, чтобы и родительский, и дочерний компоненты получали события указателя?

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

Ответ на поставленный выше вопросGestureArena.

GestureArenaиспользуется дляЖест Значение. Все идентификаторы бьются здесь и рассылаются. В любой точке экрана может быть несколько распознавателей жестов. Арена учитывает, как долго пользователь касается экрана, наклон и направление перетаскивания, чтобы определить победителя.

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

Исправление заключается в использованииGestureFactoryпри использованииRawGestureDetectorизменить производительность арены.

В качестве примера создадим простое приложение, состоящее из двух контейнеров. Цель состоит в том, чтобы и дочерний контейнер, и родительский контейнер получили жест.

использоватьRawGestureDetectorЗаверните оба контейнера. Далее мы создадим собственный распознаватель жестов.AllowMultipleGestureRecognizer.GestureRecognizerЯвляется базовым классом, от которого наследуются все остальные распознаватели. Он предоставляет базовый API для классов, чтобы они могли работать/взаимодействовать с распознавателями жестов. Примечательно,GestureRecognizerНе заботьтесь о специфике самого распознавателя.

// 自定义手势识别器。
// 重写 rejectGesture()。当一个手势被拒绝时,将调用此函数。默认情况下,它会处理
// 识别器并进行清理。但是我们修改了它,它实际上是手动添加的,以代替识别器被处理。
// 结果是你将有两个识别器在竞技场中获胜。这是双赢。

class AllowMultipleGestureRecognizer extends TapGestureRecognizer {
  @override
  void rejectGesture(int pointer) {
    acceptGesture(pointer);
  }
}

В приведенном выше коде мы создаем класс, который наследуется отTapGestureRecognizerпользовательский классAllowMultipleGestureRecognizer. Это означает, что он может наследоватьTapGestureRecognizer. В этом примере мы перепишемrejectGesture, чтобы вместо обработки распознавателя он принимал его вручную.

Теперь мы будемGestureRecognizerFactoryWithHandlersПользовательский распознаватель жестов в переданномRawGestureDetector.

Widget build(BuildContext context) {
   return RawGestureDetector(
     gestures: {
       AllowMultipleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
          AllowMultipleGestureRecognizer>(
         () => AllowMultipleGestureRecognizer(), //构造函数
         (AllowMultipleGestureRecognizer instance) { //初始化器
           instance.onTap = () => print('Episode 4 is best! (parent container) ');
         },
       )
     },

Теперь мы будемGestureRecognizerFactoryWithHandlersПользовательский распознаватель жестов в переданномRawGestureDetector. Фабричной функции требуются два свойства, конструктор и инициализатор, для создания и инициализации распознавателя жестов. Мы передаем эти параметры с помощью лямбда. Как упоминалось в приведенном выше коде, конструктор возвращаетAllowMultipleGestureRecognizerновый экземпляр , а инициализатор получает свойства, которые прослушивают нажатия и выводят некоторый текст на консоль.instance. Процесс будет повторяться для обоих контейнеров, с той лишь разницей, что будет напечатан текст.

Вот полный исходный код примера приложения:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

//主函数。 Flutter 应用的入口
void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: DemoApp(),
      ),
    ),
  );
}

//   简单的演示应用程序,由两个容器组成。目标是允许多个手势进入竞技场。
//  所有的东西都是通过 `RawGestureDetector` 和自定义 `GestureRecognizer` (继承自 `TapGestureRecognizer` )
//  将自定义 GestureRecognizer,`AllowMultipleGestureRecognizer` 添加到手势列表中,并创建一个 `AllowMultipleGestureRecognizer` 类型的 `GestureRecognizerFactoryWithHandlers`。
//  它用给定的回调创建一个手势识别器工厂函数,在这里是 `onTap`。
//  它监听 `onTap` 的一个实例,然后在被调用时向控制台打印文本。需要注意的是,`RawGestureDetector` 对于两个容器
//  是相同的。唯一的区别是打印的文本(用来标识组件)。

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: {
        AllowMultipleGestureRecognizer: GestureRecognizerFactoryWithHandlers<
            AllowMultipleGestureRecognizer>(
          () => AllowMultipleGestureRecognizer(),
          (AllowMultipleGestureRecognizer instance) {
            instance.onTap = () => print('Episode 4 is best! (parent container) ');
          },
        )
      },
      behavior: HitTestBehavior.opaque,
      //父容器
      child: Container(
        color: Colors.blueAccent,
        child: Center(
          //用 RawGestureDetector 将两个容器包裹起来
          child: RawGestureDetector(
            gestures: {
              AllowMultipleGestureRecognizer:
                  GestureRecognizerFactoryWithHandlers<
                      AllowMultipleGestureRecognizer>(
                () => AllowMultipleGestureRecognizer(),  //构造函数
                (AllowMultipleGestureRecognizer instance) {  //初始化器
                  instance.onTap = () => print('Episode 8 is best! (nested container)');
                },
              )
            },
            //在第一个容器中创建嵌套容器。
            child: Container(
               color: Colors.yellowAccent,
               width: 300.0,
               height: 400.0,
            ),
          ),
        ),
      ),
    );
  }
}

// 自定义手势识别器。
// 重写 rejectGesture()。当一个手势被拒绝时,将调用此函数。默认情况下,它会处理
// 识别器并进行清理。但是我们修改了它,它实际上是手动添加的,以代替识别器被处理。
// 结果是你将有两个识别器在竞技场中获胜。这是双赢。
class AllowMultipleGestureRecognizer extends TapGestureRecognizer {
  @override
  void rejectGesture(int pointer) {
    acceptGesture(pointer);
  }
}

Итак, каков результат выполнения приведенного выше кода?

Когда вы касаетесь желтого контейнера, оба компонента получают событие касания, поэтому на консоль выводятся два оператора.

применение:

Вывод консоли:

Что происходит, когда вы выигрываете?

После того, как жест выигран, арена будет вclosedиsweptгосударство. Это удалит неиспользуемые распознаватели и сбросит арену. Затем действие выполняется победным жестом.

вернуться к нашемуTapПример, после этого сопоставляется сonTapТеперь функция будет выполнена.

Суммировать

Сегодня мы увидели, как фреймворк Flutter обрабатывает жесты. Сначала мы рассмотрели фантастические готовые компоненты, которые Flutter предоставляет для обработки нажатий и других сенсорных событий. Далее мы обсудилиGestureDetectorИ экспериментировал с его внутренней работой. На примере мы увидели, как Flutter обрабатывает тап-жесты. мы прошли черезRawGestureDetectorЭта земля слушалаListenerголос, и к названномуGestureArenaТаинственный трибьют Flutter Fight Club.

Наконец, мы рассмотрели большую часть системы жестов во Flutter с точки зрения приложения. Вооружившись этими знаниями, вы теперь должны лучше понимать, как прикосновения к экрану приобретаются и обрабатываются за кулисами. Если у вас есть какие-либо вопросы или проблемы, пожалуйста, не стесняйтесь комментировать или черезTwitterverseСвяжитесь со мной.

такой жеОченьблагодарныйSimon Lightfoot(он же "Flutter Whisperer") внес свой вклад в эту статью ❤

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.