Основные понятия

Попробуйте Compose
Jetpack Compose – это рекомендуемый набор инструментов для создания пользовательского интерфейса в Android. Узнайте, как использовать перетаскивание в Compose.

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

Перетаскивание

Процесс перетаскивания состоит из четырех шагов или состояний: начало, продолжение, перетаскивание и завершение.

Начато

В ответ на жест перетаскивания приложение вызывает метод 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()
value
getClipDescription()
value
getLocalState()
value
getX()
value
getY()
value
getClipData()
value
getResult()
value
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, используемому с событиями касания.