Ключевые идеи

В следующих разделах объясняются несколько ключевых концепций процесса перетаскивания.

Процесс перетаскивания

В процессе перетаскивания есть четыре этапа или состояния: начало, продолжение, удаление и завершение.

Начал

В ответ на жест перетаскивания пользователя ваше приложение вызывает startDragAndDrop() чтобы сообщить системе о необходимости начать операцию перетаскивания. Аргументы метода предоставляют следующее:

  • Данные, которые необходимо перетащить.
  • Обратный вызов для рисования тени
  • Метаданные, описывающие перетаскиваемые данные
  • Система отвечает, перезвонив вашему приложению, чтобы получить тень. Затем система отображает тень на устройстве.
  • Затем система отправляет событие перетаскивания с типом действия ACTION_DRAG_STARTED прослушивателю событий перетаскивания всех объектов View в текущем макете. Чтобы продолжать получать события перетаскивания, включая возможное событие перетаскивания, прослушиватель событий перетаскивания должен возвращать true . Это регистрирует слушателя в системе. Только зарегистрированные слушатели продолжают получать события перетаскивания. На этом этапе прослушиватели также могут изменить внешний вид целевого объекта View для перетаскивания, чтобы показать, что представление может принять событие перетаскивания.
  • Если прослушиватель событий перетаскивания возвращает false , он не получает события перетаскивания для текущей операции, пока система не отправит событие перетаскивания с типом действия ACTION_DRAG_ENDED . Возвращая false , прослушиватель сообщает системе, что его не интересует операция перетаскивания и он не хочет принимать перетаскиваемые данные.
Продолжение
Пользователь продолжает перетаскивание. Когда тень перетаскивания пересекает ограничивающую рамку цели перетаскивания, система отправляет одно или несколько событий перетаскивания прослушивателю событий перетаскивания цели. Прослушиватель может изменить внешний вид целевого объекта View в ответ на событие. Например, если событие указывает, что тень перетаскивания входит в ограничивающую рамку цели перетаскивания (тип действия ACTION_DRAG_ENTERED ), прослушиватель может отреагировать, выделив View .
Упавший
Пользователь отпускает тень перетаскивания в ограничивающей рамке цели перетаскивания. Система отправляет прослушивателю цели перетаскивания событие перетаскивания с типом действия ACTION_DROP . Объект события перетаскивания содержит данные, которые передаются в систему при вызове startDragAndDrop() , который запускает операцию. Ожидается, что прослушиватель вернет системе логическое значение true , если прослушиватель успешно обработает отброшенные данные. : этот шаг выполняется только в том случае, если пользователь отбрасывает тень перетаскивания в ограничивающую рамку View , прослушиватель которого зарегистрирован для получения событий перетаскивания (цель перетаскивания). Если пользователь отпускает тень перетаскивания в любой другой ситуации, событие перетаскивания ACTION_DROP не отправляется.
Закончено

После того, как пользователь отпустит тень и система отправит

событие перетаскивания с типом действия ACTION_DROP , при необходимости система отправляет событие перетаскивания с типом действия ACTION_DRAG_ENDED чтобы указать, что операция перетаскивания завершена. Это делается независимо от того, где пользователь отпускает тень. Событие отправляется каждому прослушивателю, зарегистрированному для получения событий перетаскивания, даже если прослушиватель также получает событие ACTION_DROP .

Каждый из этих шагов более подробно описан в разделе « Операция перетаскивания» .

Перетаскивание событий

Система отправляет событие перетаскивания в форме объекта DragEvent , который содержит тип действия, описывающий, что происходит в процессе перетаскивания. В зависимости от типа действия объект может содержать и другие данные.

Прослушиватели событий перетаскивания получают объект DragEvent . Чтобы получить тип действия, слушатели вызывают DragEvent.getAction() . Существует шесть возможных значений, определенных константами в классе DragEvent , которые описаны в таблице 1:

Таблица 1. Типы действий DragEvent

Тип действия Значение
ACTION_DRAG_STARTED Приложение вызывает startDragAndDrop() и получает тень перетаскивания. Если прослушиватель хочет продолжать получать события перетаскивания для этой операции, он должен вернуть системе логическое значение true .
ACTION_DRAG_ENTERED Тень перетаскивания входит в ограничивающую рамку View прослушивателя событий перетаскивания. Это первый тип действия события, который получает прослушиватель, когда тень перетаскивания входит в ограничивающую рамку.
ACTION_DRAG_LOCATION После события ACTION_DRAG_ENTERED тень перетаскивания все еще находится в пределах ограничивающего прямоугольника View прослушивателя событий перетаскивания.
ACTION_DRAG_EXITED После ACTION_DRAG_ENTERED и хотя бы одного события ACTION_DRAG_LOCATION тень перетаскивания выходит за пределы ограничивающей рамки View прослушивателя событий перетаскивания.
ACTION_DROP Тень перетаскивания освобождается над View прослушивателя событий перетаскивания. Этот тип действия отправляется прослушивателю объекта View только в том случае, если прослушиватель возвращает логическое значение true в ответ на событие перетаскивания ACTION_DRAG_STARTED . Этот тип действия не отправляется, если пользователь отпускает тень перетаскивания над View , прослушиватель которого не зарегистрирован, или если пользователь отпускает тень перетаскивания над чем-либо, что не является частью текущего макета.

Прослушиватель возвращает логическое значение true , если он успешно обработал удаление. В противном случае он должен вернуть false .

ACTION_DRAG_ENDED Система завершает операцию перетаскивания. Этому типу действия не обязательно предшествует событие ACTION_DROP . Если система отправляет ACTION_DROP , получение типа действия ACTION_DRAG_ENDED не означает, что удаление удалось. Слушатель должен вызвать getResult() , как показано в таблице 2 , чтобы получить значение, возвращаемое в ответ на ACTION_DROP . Если событие ACTION_DROP не отправлено, getResult() возвращает false .

Объект DragEvent также содержит данные и метаданные, которые ваше приложение предоставляет системе при вызове startDragAndDrop() . Некоторые данные действительны только для определенных типов действий, как показано в таблице 2. Дополнительные сведения о событиях и связанных с ними данных см. в разделе « Операция перетаскивания» .

Таблица 2. Допустимые данные DragEvent по типам действий

getAction()
ценить
getClipDescription()
ценить
getLocalState()
ценить
getX()
ценить
getY()
ценить
getClipData()
ценить
getResult()
ценить
ACTION_DRAG_STARTED
ACTION_DRAG_ENTERED
ACTION_DRAG_LOCATION
ACTION_DRAG_EXITED
ACTION_DROP
ACTION_DRAG_ENDED

Методы DragEvent getAction() , describeContents() , writeToParcel() и toString() всегда возвращают действительные данные.

Если метод не содержит допустимых данных для определенного типа действия, он возвращает null или 0, в зависимости от типа результата.

Перетащите тень

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

Изображение называется тенью . Вы создаете его с помощью методов, объявленных для объекта View.DragShadowBuilder . Вы передаете построитель системе при запуске операции перетаскивания с помощью startDragAndDrop() . В рамках ответа на startDragAndDrop() система вызывает методы обратного вызова, определенные вами в View.DragShadowBuilder для получения тени перетаскивания.

Класс View.DragShadowBuilder имеет два конструктора:

View.DragShadowBuilder(View)

Этот конструктор принимает любые объекты View вашего приложения. Конструктор сохраняет объект View в объекте View.DragShadowBuilder , поэтому обратные вызовы могут получить к нему доступ для создания тени перетаскивания. Представление не обязательно должно быть View , которое пользователь выбирает для запуска операции перетаскивания.

Если вы используете этот конструктор, вам не нужно расширять View.DragShadowBuilder или переопределять его методы. По умолчанию вы получаете тень, которая имеет тот же вид, что и View , которое вы передаете в качестве аргумента, с центром под местом, где пользователь касается экрана.

View.DragShadowBuilder()

Если вы используете этот конструктор, объект View не будет доступен в объекте View.DragShadowBuilder . Поле установлено в значение null . Вы должны расширить View.DragShadowBuilder и переопределить его методы, иначе вы получите невидимую тень при перетаскивании. Система не выдает ошибку.

Класс View.DragShadowBuilder имеет два метода, которые вместе создают тень перетаскивания:

onProvideShadowMetrics()

Система вызывает этот метод сразу после вызова startDragAndDrop() . Используйте этот метод, чтобы отправить в систему размеры и точку касания тени. Метод имеет два параметра:

outShadowSize : объект Point . Ширина тени при перетаскивании указывается в x , а ее высота — в y .

outShadowTouchPoint : объект Point . Точка касания — это место в тени перетаскивания, которое должно находиться под пальцем пользователя во время перетаскивания. Его позиция X переходит в x , а позиция Y — в y .

onDrawShadow()

Сразу после вызова onProvideShadowMetrics() система вызывает onDrawShadow() для создания тени при перетаскивании. У метода есть единственный аргумент — объект Canvas , который система конструирует на основе параметров, которые вы предоставляете в onProvideShadowMetrics() . Метод рисует тень на предоставленном Canvas .

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

Перетащите прослушиватели событий и методы обратного вызова

View получает события перетаскивания с помощью прослушивателя событий перетаскивания, который реализует View.OnDragListener , или с помощью метода обратного вызова представления onDragEvent() . Когда система вызывает метод или прослушиватель, она предоставляет аргумент DragEvent .

В большинстве случаев использование прослушивателя предпочтительнее использования метода обратного вызова. При разработке пользовательских интерфейсов вы обычно не создаете подклассы классов View , но использование метода обратного вызова заставляет вас создавать подклассы для переопределения метода. Для сравнения, вы можете реализовать один класс прослушивателя, а затем использовать его с несколькими различными объектами View . Вы также можете реализовать его как анонимный встроенный класс или лямбда-выражение. Чтобы установить прослушиватель для объекта View , вызовите setOnDragListener() .

В качестве альтернативы вы можете изменить реализацию onDragEvent() по умолчанию, не переопределяя метод. Установите OnReceiveContentListener для представления; для получения более подробной информации см. setOnReceiveContentListener() . Затем метод onDragEvent() по умолчанию выполняет следующее:

  • Возвращает true в ответ на вызов startDragAndDrop() .
  • Вызывает performReceiveContent() , если данные перетаскивания перетаскиваются в представление. Данные передаются методу как объект ContentInfo . Метод вызывает OnReceiveContentListener .

  • Возвращает true, если данные перетаскивания перетаскиваются в представление и OnReceiveContentListener потребляет любое содержимое.

Определите OnReceiveContentListener для обработки данных специально для вашего приложения. Для обратной совместимости до уровня API 24 используйте версию OnReceiveContentListener для Jetpack.

У вас может быть прослушиватель событий перетаскивания и метод обратного вызова для объекта View , и в этом случае система сначала вызывает прослушиватель. Система не вызывает метод обратного вызова, если прослушиватель не возвращает false .

Комбинация метода onDragEvent() и View.OnDragListener аналогична комбинации onTouchEvent() и View.OnTouchListener , используемой с событиями касания.