Общие вопросы и теория технологии Drag and Drop. Технология Drag and Drop в Android Смотреть что такое "Drag-and-drop" в других словарях

182

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

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

Взаимодействие Draggable реализуется исключительно за счет использования специфической HTML-разметки и CSS-стилей. Это означает, что данная функциональность будет работать практически в любом браузере, но наделенные ею элементы не смогут работать с аналогичными собственными средствами Drag-and-drop операционных систем.

Определяемые спецификацией HTML5 операции Drag-and-drop обычно реализуются с использованием собственных механизмов операционных систем. Если вы используете механизм Drag-and-drop jQuery UI, то во избежание возникновения конфликтных ситуаций эквивалентные средства HTML5 лучше отключить. С этой целью установите для атрибута draggable элемента body документа значение false.

Настройка взаимодействия Draggable

Существует множество опций настройки для взаимодействия Draggable. Наиболее важные свойства, рассмотрению которых посвящены следующие разделы, приведены в таблице ниже:

Свойства взаимодействия Draggable Свойство Описание
axis Ограничивает возможности перемещения определенными направлениями. Значение по умолчанию - false, оно означает отсутствие ограничений, но можно также указать значение "x" (перемещение только вдоль оси X) или "y" (перемещение только вдоль оси Y)
containment Ограничивает местоположение перемещаемого элемента определенной областью экрана. Типы поддерживаемых значений описаны в таблице ниже, при рассмотрении соответствующего примера. Значение по умолчанию - false, оно означает отсутствие ограничений
delay Определяет время, в течение которого должно осуществляться перетаскивание элемента, прежде чем он переместится. Значение по умолчанию - 0, оно означает отсутствие задержки
distance Определяет расстояние, на которое пользователь должен перетащить элемент из его начальной позиции, прежде чем он действительно переместится. Значение по умолчанию - 1 пиксель
grid Осуществляет принудительную привязку перемещаемого элемента к ячейкам сетки. Значение по умолчанию - false, оно означает отсутствие привязки
Ограничение направлений перемещения

Существуют несколько способов, с помощью которых можно ограничить перемещение элемента определенными направлениями. Первый из них заключается в использовании опции axis, позволяющей ограничить направление перемещения осью X или Y. Соответствующий пример приведен ниже:

... div.dragElement {font-size: large; border: thin solid black; padding:16px; width: 8em; text-align: center; background-color: lightgray; margin: 4px } $(function() { $(".dragElement").draggable({ axis: "x" }).filter("#dragV").draggable("option", "axis", "y"); }); Перетащить по вертикали Перетащить по горизонтали Запустить пример

В этом примере мы определяем два элемента div, выбираем их с помощью jQuery и вызываем метод draggable(). В качестве аргумента этому методу передается объект, который первоначально ограничивает перемещение обоих элементов div направлением вдоль оси X. Применив затем метод jQuery filter(), мы получаем возможность выбрать элемент dragV без повторного поиска средствами jQuery по всему документу и установить для него другое разрешенное направление перемещения - вдоль оси Y. Таким образом, мы получаем документ, в котором один элемент div можно перетаскивать только в вертикальном направлении, а другой - только в горизонтальном. Результат представлен на рисунке:

Ограничение допустимой области перемещения элемента

Можно также ограничить область экрана, в которой допускается перетаскивание элемента. Для этого используется опция containment. Форматы значений, которые можно указывать в этой опции, описаны в таблице ниже:

Пример использования опции containment приведен ниже:

... div.dragElement {font-size: large; border: thin solid black; padding:16px; width: 8em; text-align: center; background-color: lightgray; margin: 4px } #container { border: medium double black; width: 700px; height: 450px} $(function() { $(".dragElement").draggable({ containment: "parent" }).filter("#dragH").draggable("option", "axis", "x"); }); Перетащить по горизонтали Перетащить внутри родителя Запустить пример

В этом примере возможности перемещения обоих элементов ограничены таким образом, что их можно перетаскивать только внутри родительского элемента, в качестве которого выступает элемент div с фиксированными размерами. Для одного из перемещаемых элементов div с помощью опции axis введено дополнительное ограничение, заключающееся в том, что он может перемещаться внутри родительского элемента только в горизонтальном направлении. Результат проиллюстрирован на рисунке:

Ограничение возможностей перемещения элемента ячейками сетки

Опция grid позволяет задать привязку перемещаемого элемента к ячейкам сетки. Эта опция принимает в качестве значения массив из двух элементов, определяющих ширину и высоту ячеек сетки в пикселях. Пример использования опции grid приведен ниже:

... #draggable {font-size: x-large; border: thin solid black; width: 5em; text-align: center; padding:10px} $(function() { $("#draggable").draggable({ grid: }); }); Перетащи меня Запустить пример

В этом примере задана сетка с ячейками шириной 100 пикселей и высотой 50 пикселей. Когда вы перетаскиваете элемент, он "перескакивается" из одной (невидимой) ячейки в другую. Эффект привязки является весьма показательным примером использования функциональности взаимодействий, однако его трудно передать с помощью экранных снимков.

Можно создать эффект привязки лишь для одного направления, указав для оси свободного перемещения значение 1. Например, если присвоить опции grid значение , то элемент будет привязываться к ячейкам сетки шириной 100 пикселей при перемещении по горизонтали, но перемещение по вертикали будет свободным.

Задержка перемещения

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

Пример использования обеих настроек приведен ниже:

... #time, #distance {font-size: large; border: thin solid black; padding: 10px; width: 120px; text-align: center; background-color: lightgray; margin: 4px; } $(function() { $("#time").draggable({ delay: 1000 }) $("#distance").draggable({ distance: 150 }) }); Блок с задержкой времени Блок с минимальным расстоянием Запустить пример

В этом примере есть два перемещаемых элемента, для одного из которых задержка задана с помощью опции delay, а для другого - с помощью опции distance.

В случае задержки, определяемой опцией delay, пользователь должен выполнять перетаскивание в течение заданного времени, прежде чем это приведет к действительному перемещению элемента. В данном примере длительность этого промежутка составляет 1000 мс. Перемещать мышь в это время вовсе не обязательно, но на протяжении всего периода задержки кнопка мыши должна оставаться в нажатом состоянии, после чего элемент можно будет переместить, сдвинув мышь. По истечении времени задержки перемещаемый элемент привяжется к местоположению указателя мыши с учетом ограничений, налагаемых опциями grid, region и axis, о которых ранее говорилось.

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

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

Использование методов взаимодействия Draggable

Все методы, определенные для взаимодействия Draggable, входят в набор базовых методов, с которыми вы уже познакомились при рассмотрении виджетов. Методы, специфические для взаимодействия Draggable, не предусмотрены, поэтому мы не будем рассматривать их подробно. Перечень доступных методов приведен в таблице ниже:

Использование событий взаимодействия Draggable

Взаимодействие Draggable поддерживает простой набор событий, уведомляющих о перетаскивании элемента. Эти события описаны в таблице ниже:

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

... #draggable {font-size: x-large; border: thin solid black; width: 190px; text-align: center; padding:10px} $(function() { $("#draggable").draggable({ start: function() { $("#draggable").text("Перетаскивание...") }, stop: function() { $("#draggable").text("Перетащи меня") } }); }); Перетащи меня Запустить пример

В этом примере события start и stop используются для изменения текстового содержимого элемента в процессе перетаскивания. Эта благоприятная возможность является следствием того, что взаимодействие Draggable реализовано исключительно с использованием средств HTML и CSS: можно использовать jQuery для изменения состояния перемещаемого элемента даже в то время, когда он движется по экрану.

Использование взаимодействия Droppable

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

Элементы, к которым было применено взаимодействие Droppable (принимающие элементы), приобретают способность принимать перемещаемые элементы, созданные с помощью взаимодействия Draggable.

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

События взаимодействия Droppable Событие Описание
create Происходит в момент применения взаимодействия Droppable к элементу
activate Происходит, когда пользователь начинает перетаскивать перемещаемый элемент
deactivate Происходит, когда пользователь прекращает перетаскивать перемещаемый элемент
over Происходит, когда пользователь перетаскивает перемещаемый элемент над принимающим элементом (но при условии, что кнопка мыши еще не была отпущена)
out Происходит, когда пользователь перетаскивает перемещаемый элемент за пределы принимающего элемента
drop Происходит, когда пользователь оставляет перемещаемый элемент на принимающем элементе

Пример создания простого принимающего элемента, для которого определен единственный обработчик события drop, приведен ниже:

... #draggable, #droppable {font-size: large; border: thin solid black; padding: 10px; width: 100px; text-align: center; background-color: lightgray; margin: 4px;} #droppable {padding: 20px; position: absolute; right: 5px;} $(function() { $("#draggable").draggable(); $("#droppable").droppable({ drop: function() { $("#draggable").text("Оставлено") } }); }); Оставь здесь Перетащи меня Запустить пример

В этом примере в документ добавлен элемент div, текстовое содержимое которого представлено строкой "Оставь здесь". Мы выбираем этот элемент, используя jQuery, и вызываем метод droppable(), передавая ему объект с настройками, который определяет обработчик для события drop. Ответом на это событие является изменение текста перемещаемого элемента с помощью метода text().

Создаваемое в данном примере интерактивное взаимодействие категории Drag-and-drop является простейшим, но оно создает удобный контекст для объяснения возможностей совместной работы взаимодействий Draggable и Droppable. Различные стадии процесса перетаскивания элементов проиллюстрированы на рисунке:

Все это выглядит очень просто. Мы перетаскиваем перемещаемый элемент до тех пор, пока он не окажется над принимающим элементом, и отпускаем его. Перемещаемый элемент остается там, где он был оставлен, и его текстовое содержимое изменяется в ответ на наступление события drop. В следующих разделах показано, как использовать другие события взаимодействия Droppable для повышения комфортности работы пользователя.

Подсветка целевого принимающего объекта

Используя события activate и deactivate, можно подсветить целевой принимающий объект, когда пользователь начинает процесс перетаскивания элемента. Во многих ситуациях эта идея оказывается весьма плодотворной, поскольку при этом пользователь получает надежное указание относительно того, какие элементы являются частью модели Drag-and-drop. Соответствующий пример приведен ниже:

... $(function() { $("#draggable").draggable(); $("#droppable").droppable({ drop: function() { $("#draggable").text("Оставлено") }, activate: function() { $("#droppable").css({ border: "medium double green", backgroundColor: "lightGreen" }); }, deactivate: function() { $("#droppable").css("border", "").css("background-color", ""); } }); }); ... Запустить пример

Как только пользователь начинает перетаскивать элемент, срабатывает событие activate - связанное с нашим принимающим элементом, и функция-обработчик использует метод css() для изменения CSS-свойств border и background-color этого элемента. В результате целевой принимающий элемент подсвечивается, указывая пользователю на существование связи между ним и перемещаемым элементом.

Событие deactivate используется для удаления значений CSS-свойств из принимающего элемента и его возврата в исходное состояние, как только пользователь отпускает кнопку мыши. (Это событие происходит всякий раз, когда перетаскивание элемента прекращается, независимо от того, оставлен перемещаемый элемент на принимающем элементе или не оставлен.) Этот процесс проиллюстрирован на рисунке:

Обработка перекрывания элементов

Технологию Drag-and-drop можно усовершенствовать, добавив в нее обработку событий over и out. Событие over происходит, когда 50% перемещаемого элемента оказывается над любой частью принимающего элемента. Событие out наступает тогда, когда перекрывавшиеся ранее элементы перестают перекрываться. Пример ответной реакции на эти события приведен ниже:

$(function() { $("#draggable").draggable(); $("#droppable").droppable({ drop: function() { $("#draggable").text("Оставлено") }, activate: function() { $("#droppable").css({ border: "medium double green", backgroundColor: "lightGreen" }); }, deactivate: function() { $("#droppable").css("border", "").css("background-color", ""); }, over: function() { $("#droppable").css({ border: "medium double red", backgroundColor: "red" }); }, out: function() { $("#droppable").css("border", "").css("background-color", ""); } }); }); Запустить пример

Здесь использованы те же функции-обработчики, что и в предыдущем примере, но в данном случае они связаны с событиями over и out. Когда с принимающим элементом перекрывается по крайней мере 50% перемещаемого элемента, он заключается в рамку и цвет его фона изменяется, как показано на рисунке:

Указанный 50%-ный предел называется порогом перекрывания (tolerance) , величину которого можно задавать при создании принимающего элемента, как будет показано далее.

Настройка взаимодействия Droppable

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

Свойства взаимодействия Droppable Свойство Описание
disabled Если эта опция равна true, то функциональность взаимодействия Droppable первоначально отключена. Значение по умолчанию - false
accept Сужает множество перемещаемых элементов, на которые будет реагировать принимающий элемент. Значение по умолчанию - *, ему соответствует любой элемент
activeClass Определяет класс, который будет присваиваться в ответ на событие activate и удаляться в ответ на событие deactivate
hoverClass Определяет класс, который будет присваиваться в ответ на событие over и удаляться в ответ на событие out
tolerance Определяет минимальную степень перекрывания, при которой происходит событие over
Ограничение допустимых перемещаемых элементов

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

... .draggable, #droppable {font-size: large; border: thin solid black; padding: 10px; width: 100px; text-align: center; background-color: lightgray; margin: 4px;} #droppable {padding: 20px; position: absolute; right: 5px;} $(function() { $(".draggable").draggable(); $("#droppable").droppable({ drop: function(event, ui) { ui.draggable.text("Оставлено") }, activate: function() { $("#droppable").css({ border: "medium double green", backgroundColor: "lightGreen" }); }, deactivate: function() { $("#droppable").css("border", "").css("background-color", ""); }, accept: "#drag1" }); }); Оставь здесь Элемент 1 Элемент 2 Запустить пример

В этом примере есть два перемещаемых элемента с идентификаторами drag1 и drag2. При создании принимающего элемента используется опция accept, с помощью которой мы указываем, что приемлемым перемещаемым элементом будет только элемент drag1.

При перетаскивании элемента drag1 вы будете наблюдать тот же эффект, что и в предыдущих примерах. В соответствующие моменты для принимающего элемента будут запускаться события activate, deactivate, over и out. В то же время, если перетаскивать элемент drag2, который не соответствует указанному в параметре accept селектору, то эти события запускаться не будут. Этот элемент можно свободно перемещать, но он не будет восприниматься принимающим элементом.

Обратите внимание на изменение способа выбора приемлемого перемещаемого элемента, для которого следует вызывать метод text(). Когда в документе был всего лишь один перемещаемый элемент, для этого хватало атрибута id:

Drop: function() { $("#draggable").text("Оставлено") },

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

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

Drop: function(event, ui) { ui.draggable.text("Оставлено") },

Изменение порога перекрывания

По умолчанию событие over происходит лишь в тех случаях, когда по крайней мере 50% перемещаемого элемента перекрывается с принимающим элементом. Величину этого порогового перекрывания можно изменить с помощью опции tolerance, которая может принимать значения, указанные в таблице ниже:

Чаще всего я использую два значения, fit и touch, поскольку их смысл наиболее понятен для пользователей. Значение fit используется мною в тех случаях, когда перетаскиваемый элемент должен остаться в той области принимающего элемента, в которую он был перемещен, а значение touch - когда перемещенный элемент должен вернуться в исходную позицию (соответствующий пример будет приведен далее). Пример использования параметров fit и touch приведен ниже:

Значение clone указывает jQuery UI на то, что необходимо создать копию перемещаемого элемента вместе со всем его содержимым и использовать полученный результат в качестве вспомогательного элемента. Результат представлен на рисунке:

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

Как показано на рисунке, исходный перемещаемый элемент остается на своем месте и лишь вспомогательный элемент перемещается по экрану вслед за указателем мыши. Если размеры перемещаемого элемента велики, как в нашем примере, то он закрывает собой остальные элементы документа, так что даже отследить позицию принимающего элемента пользователю будет трудно. С этой проблемой можно справиться, предоставив функцию в качестве значения опции helper, как показано в примере ниже:

... $(function() { $("div.draggable")..png"/>") } }); $("#basket").droppable({ activeClass: "active", hoverClass: "hover" }); }); ... Запустить пример

Когда пользователь начинает перетаскивать элемент, jQuery UI вызывает функцию, заданную параметром helper, и использует возвращаемый элемент в качестве перемещаемого объекта. В данном случае я использую jQuery для создания элемента img. Результат представлен на рисунке:

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

Объект ui, который jQuery UI передает событиям взаимодействия Droppable, содержит свойство helper, и это свойство можно использовать для манипуляций вспомогательным элементом в процессе его перетаскивания. Пример использования этого свойства в связке с событиями over и out приведен ниже:

... $(function() { $("div.draggable")..png"/>") } }); $("#basket").droppable({ activeClass: "active", hoverClass: "hover", over: function(event, ui) { ui.helper.css("border", "thick solid #27e6ed") }, out: function(event, ui) { ui.helper.css("border", "") } }); }); ...

Здесь события over и out, а также свойство ui.helper используются для отображения рамки вокруг вспомогательного элемента, когда он перекрывает принимающий элемент. Результат представлен на рисунке:

Привязка к краям элементов

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

Запустить пример jQuery UI #snapper, .draggable, .droppable {font-size: large; border: medium solid black; padding: 4px; width: 150px; text-align: center; background-color: lightgray; margin-bottom: 10px;} .droppable {margin-right: 5px; height: 50px; width: 120px} #dropContainer {position: absolute; right: 5px;} div span {position: relative; top: 25%} .droppable.active {border: medium solid green} .droppable.hover {background-color: lightgreen} #snapper {position: absolute; left: 35%; border: medium solid black; width: 180px; height: 50px} $(function() { $("div.draggable").draggable({ snap: "#snapper, .droppable", snapMode: "both", snapTolerance: 50 }); $("#basket").droppable({ activeClass: "active", hoverClass: "hover" }); }); Корзина Привяжись здесь Перетащи меня

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

Существует пара вспомогательных опций, позволяющих более точно настроить поведение элементов в отношении привязки. Одна из них - это опция snapMode . С ее помощью можно указать тип привязки. Допускаются следующие значения: inner (привязка к внутренним краям элементов), outer (привязка к внешним краям элементов) и both (привязка ко всем краям; используется по умолчанию).

Опция snapTolerance позволяет указать, на какое расстояние должен приблизиться перемещаемый элемент к краю элемента-мишени, прежде чем произойдет привязка. Значение по умолчанию - 20, что означает 20 пикселей. В примере используется значение 50, которому соответствует привязка на большем расстоянии. Очень важно правильно выбрать значение этой опции. Если значение опции snapTolerance слишком мало, то пользователь может не заметить эффекта привязки, а если оно слишком велико, то перемещаемый элемент начнет совершать неожиданные скачки, привязываясь к далеко расположенным элементам.

Использование технологии перетаскивания (drag and drop ) дает возможность пользователю перемещать различные объекты из одного в другой, например, элементы одного списка в другой. Для этого необходимо использовать два элемента управления: приемник и источник. Приемником считается тот объект, который примет объект источник (перемещаемый объект).

События, возникающие в процессе перемещения объектов, перечислены ниже в том порядке в каком они возникают.

OnStartDrag (тип TStartDragEvent) - в начале выполнения операции генерируется объектом-источником. Параметры, которые передаются обработчику событий: объект приемник DragObject (тип TDragObject), объект-источник Source (тип TObject).

OnDragOver (тип TDragOverEvent) - создает объект-приемник, когда над ним оказывается перемещаемый объект. Параметры, которые передаются обработчику событий:объект-приемник Sender (тип TObject), объект-источник Source (тип TObject), состояние перемещения State (тип TDragState), X и Y (тип integer) - текущие координаты указателя мыши, Accept (тип boolean) признак подтверждения операции перемещения. Состояние перемещения дает понять находиться ли перемещаемый объект в области приемника, передвигается ли в ней, покинул ее. Переданные параметры дают возможность объекту-приемнику принять или отклонить объект-источник. Параметр Accept устанавливается в значение Trye если операция перемещения принята, в противном случае - False.

onDragDrop (тип TDragDropEvent) - создается объектом-приемником, когда перемещаемый объект опускается на нем. Обработчику события передаются текущие координаты указателя мыши, объект приемник Sender (тип TObject), исходный объект перемещения Source (тип TObject).

onEndDrag (тип EndDragEvent) - создается при завершении операции перетаскивания. Передаются обработчику события координаты X и Y точки, где оказался объект-источник Sender и объект приемник Target.

Для создания перетаскивания достаточно реализовать два события: OnDragDrop и OnDragOver при установленном свойстве DragMode равном dmAutomatic. В противном случае начало операции перемещения, метод BeginDrag, необходимо программисту закодировать.

Для закрепления материала создадим следующее приложение. На форму поместим компонент Panel. В свойстве DragMode Инспектора Объектов установим значение dmAutomatic. Выделим объект форма и с помощью Инспектора Объектов создадим нижеследующие события:

Procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin if Source = Panel1 then Accept:= True else Accept:= False; end; procedure TForm1.FormDragDrop(Sender, Source: TObject; X, Y: Integer); begin Panel1.Left:= X; Panel1.Top:= Y; end;

Теперь запустив приложение и нажав над панелью кнопку мыши мы можем перемещать объект панель по всей форме.

Итог: мы познакомились с технологией перетаскивания (drag and drop) и использовали ее на практике.

Методы работы с техникой "перетащи и брось" складывались в течение многих лет. Неудивительно, что с увеличением числа программистов, разрабатывающих плагины с открытым исходным кодом (например, для jQuery) вновь возрождаются старые методы. Библиотека для JavaScript весьма адаптивна и предлагает множество улучшений в нашу эпоху веб технологий.

В данном уроке мы сделаем скрипт, который можно будет использовать для создания динамических прямоугольников с техникой "перетащи и брось" на своем веб сайте. Процесс управляется jQuery. Такие скрипты сохраняют время, предоставляя готовый функционал! А библиотеку "перетащи и брось" можно будет использовать в других проектах.

Подготавливаем контент

Первым делом подготовим небольшой сайт для проекта. В папке проекта нужно создать два каталога с примечательными именами"js" и "css" и пустой файл index.html . Код будет очень простой, чтобы сложилось четкое представление о работе, и появилась точка для дальнейшего развития.

Ниже приводится код нашего HTML файла. В разделе head мы включаем 3 скрипта. Основной скрипт jQuery будет подгружаться с сервера Google Code . также подключается наш файл стилей style.css , который содержит основные свойства для формирования внешнего вида нашего документа.

Перетащи меня Да-да. Именно меня. Меня тоже можно перетащить { zIndex: 200, opacity: .9 }

P.S.: меня можно бросить где угодно!

Внутри раздела body помещены только два блока div , которые содержат оба прямоугольника. Код достаточно простой и понятный. Внутри каждого прямоугольника размещены заголовки с классами handler и handler2 . Это важно, так как при перетаскивании каждый прямоугольник ведет себя по-своему.


Устанавливаем CSS

Код HTML очень прост. Если вам понятна основная разметка, то и стили CSS также не будут представлять сложности. В основном определяются поля, отступы и цвета.

Body,html { font-family:Calibri, sans-serif; background:#eaf3fb; font-size:12px; height:1000px; line-height:18px; } p { height:30px; }

Селекторы body,html используются только для демонстрационной страницы. А весь контент размещается в двух перетаскиваемых прямоугольниках.

Dv1 { width:200px; background-color:#eff7ff; border:1px solid #96c2f1; position:absolute; left:100px; top:100px; } .dv1 h2 { background-color:#b2d3f5; padding:5px; font-family:Georgia, "Times New Roman", Times, serif; font-size:1.0em; text-transform:uppercase; font-weight:bold; color:#3a424a; margin:1px; cursor:move; } .dv1 div { padding:5px; margin-bottom:10px; } .dv2 { background-color:#f6ebfb; border:1px solid #a36fde; width:550px; position:absolute; cursor:move; left:400px; top:230px; } .dv2 h2 { background-color:#eacfe9; letter-spacing:-0.09em; font-size:1.8em; font-weight: bold; padding:15px; margin:1px; color:#241f24; cursor:move; } .dv2 .content2 { padding:5px; margin-bottom:10px; }

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

Также шрифты и цвета различаются для прямоугольников, чтобы легче было увидеть разницу.

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

Разбираем JavaScript

Два файла JavaScript содержат весь необходимый для работы код. Опустим детали работы с jQuery, так как это выходит за рамки урока. Обратим внимание на файл jquery.dragndrop.js .

На строке 22 происходит определение функции Drags .

$.fn.Drags = function(opts) { var ps = $.extend({ zIndex: 20, opacity: .7, handler: null, onMove: function() { }, onDrop: function() { } }, opts);

Здесь устанавливается возвращаемая переменная и данные инициализации для Drags . Такой способ очень широко используется при работе с jQuery для передачи опций другим функциям. Внутри мы устанавливаем переменные для всех доступных опций для перетаскиваемых прямоугольников.


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

Var dragndrop = { drag: function(e) { var dragData = e.data.dragData; dragData.target.css({ left: dragData.left + e.pageX - dragData.offLeft, top: dragData.top + e.pageY - dragData.offTop }); dragData.handler.css({ cursor: "move" }); dragData.target.css ({ cursor: "move" }); dragData.onMove(e); }, drop: function(e) { var dragData = e.data.dragData; dragData.target.css(dragData.oldCss); //.css({ "opacity": "" }); dragData.handler.css("cursor", dragData.oldCss.cursor); dragData.onDrop(e); $().unbind("mousemove", dragndrop.drag) .unbind("mouseup", dragndrop.drop); } }

Наши функции манипулируют CSS позиционированием каждого объекта. Если изменить абсолютное позиционирование ваших объектов, то это не повлияет на работу кода, так как каждая функция JavaScript изменяет любой стиль, который определен для объекта.

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

Функции Drag/Drop

Во втором файле fn.js содержится совсем простой код. Мы дожидаемся полной загрузки документа, после чего вызываем наши функции. Определяются два экземпляра функции Drags , которая разбиралась ранее.

У нас есть два перемещаемых блока с классами.dv1 и.dv2 . Если нужно оставить один перемещаемый блок, то нужно просто удалить вторую часть кода. Добавление еще одного перемещаемого блока также осуществляется просто. Нужно только добавить новую функцию в данном файле.

Первым делом нужно установить опции при вызове функции. Обязательно надо установить имя обработчика. C его помощью мы сообщаем jQuery, какой обработчик использовать при нажатии кнопки мыши в определенной области документа. В качестве имени обработчика может быть класс или атрибут ID.

В нашей первой функции есть два обработчика событий onMove и onDrop . Оба вызывают новые функции, передаваемые в текущее событие как переменные. Здесь выполняется манипулирование кодом HTML в прямоугольнике для обновления при каждом движении. Это замечательный эффект для демонстрации того, как можно осуществлять управление процессом с помощью простых событий jQuery.

Во второй функции мы используем параметры z-Index и opacity . Можно добавить и другие свойства CSS? но для этого потребуется переработать код JavaScript, чтобы осуществлялась проверка установок. Например, можно передавать другой стиль шрифта или значения для высоты и ширины для перемещаемого прямоугольника - получится очень интересный трюк!

Заключение

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

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

Так что изучайте документацию jQuery для использования библиотечных функций.

Для библиотеки VCL фирмой Borland реализована собственная версия интерфейса Drag&Drop (переводится как "перетащить"). Интерфейс этот внутренний - передавать и принимать можно любые управляющие элементы Delphi внутри формы" (кроме самой формы). Он реализован без использования соответствующих функций API Windows - их нужно применять при организации общения с другими задачами путем перетаскивания.

Нажав левую кнопку мыши над элементом управления, мы можем "перетащить" его на любой другой элемент. С точки зрения программиста это означает, что в моменты перетаскивания и отпускания клавиши генерируются определенные события, которыми передается вся необходимая информация - указатель на перетаскиваемый объект, текущие координаты курсора и др. Получателем событий является тот элемент, на котором в данный момент находится курсор. Обработчик такого события должен сообщить системе, принимает ли данньш элемент управления "посылку" или нет. При отпускании кнопки над принимающим элементом управления генерируется еще одно или два события, в зависимости от готовности приемника.

CancelDrag Отменяет текущую drag-and-drop или drag-and-dock операцию.

Функция FindDragTarget (const Pos: TPoint ;AllowDisabled: Boolean ): TControl ;

Функция возвращает объект базового класса TControl , к которому относится позиция экрана с координатами, определенными параметром Pos. Данная функция используется для определения потенциального получателя drag-and-drop или drag-and-dock операции. Если для указанной позиции не существует никакого оконного средства управления, то функция возвращает nil . Параметр AllowDisabled определяет, будут ли учитываться заблокированные (disabled) объекты.

Функция IsDragObject (Sender: TObject ): Boolean ;

Функция определяет, является ли объект, определенный в параметре Sender, потомком класса TDragObject . Данную функцию можно использовать в качестве параметра Source в обработчиках событий OnDragOver и OnDockOver для того, чтобы определить будет ли принят перетаскиваемый объект. Также функцию IsDragObject можно использовать в качестве параметра Source в обработчиках событий OnDragDrop и OnDockDrop для того, чтобы правильно интерпретировать перетаскиваемый объект.

Свойства DragMode, DragCursor, методы BeginDrag, OnDragOver, OnDragDrop, OnEndDrag, OnStartDrag, параметр Accept

Процесс перетаскивания с помощью мыши информации из одного объекта в другой широко используется в Widows.Можно перемещать файлы между папками, перемещать сами папки и др.

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

Начало перетаскивания определяется свойством DragMode, которое может устанавливаться в процессе проектирования или программно равным dmManual или dmAutomatic. Значение dmAutomatic (автоматическое) определяет автоматическое начало процесса перетаскивания при нажатии пользователем кнопки мыши над компонентом. Однако в этом случае событие OnMouseDown, связанное с нажатием пользователем кнопки мыши, для этого компонента вообще не наступает.

Интерфейс переноса и приема компонентов появился достаточно давно. Он обеспечивает взаимодействие двух элементов управления во время выполнения приложения. При этом могут выполняться любые необходимые операции. Несмотря на простоту реализации и давность разработки, многие программисты (особенно новички) считают этот механизм малопонятным и экзотическим. Тем не менее использование Drag-and-Drop может оказаться очень полезным и простым в реализации. Сейчас мы в этом убедимся.

Для того чтобы механизм заработал, требуется настроить соответствующим образом два элемента управления. Один должен быть источником (Source), второй - приемником (Target). При этом источник никуда не перемещается, а только регистрируется в качестве такового в механизме.

Поверьте, достаточно просто преобразовать X,Y координаты, передаваемые в параметрах событий OnDragOver и OnDragDrop, в координаты формы.

Работайте со свойствами Left и Top компонента, над которым перемещается курсор. Приведу простой пример. Поместите на форму компонент Memo и присвойте свойству Align значение alTop. Поместите на форму панель, также присвойсте свойству Align значение alTop и задайте небольшое значение свойству Height, скажем 6 или 7 пикселей. Установите DragMode на dmAutomatica и DragCursor на crVSplit. Поместите другой Memo-компонент и установите Align на alClient. Одновременно выберите оба Memo-компонента, панель и создайте общий обработчик события OnDragOver как показано ниже:

Недавно у меня появилась идея заняться разработкойигры для андроид . Для начала я решил написать шахматы. Мне казалось технологияDrag and Drop отлично подойдет для реализации механизма перемещения фигур. Для непосвященных отмечу, чтометод drag and drop заключается в возможности перетаскивания одних графических объектов на другие и выполнения того или иного действия после отпускания. Простейший пример - удаление ярлыка с рабочего стола вашего ПК перетаскиванием его в корзину. "Кинув" ярлык в корзину, мы говорим системе, что хотим заставить взаимодействовать эти два объекта. Система получает наш сигнал и решает, какое действие ей стоит предпринять. Drag and drop получила широкое распространение благодаря своей интуитивной ясности. Этот подход подкреплен нашим опытом взаимодействия с объектами реального мира и прекрасно работает в виртуальной среде. Что же касается шахмат, с помощью drag and drop технологически проще определить клетку, куда пользователь перетащил фигуру, поскольку не нужно вычислять номер клетки по координатам точки отпускания. Эту работу возьмет на себя виртуальная машина.

Цели использования технологии Drag n Drop

Использование технологии drag and drop позволяет мне малой кровью решить три задачи:

  • Визуализация хода. Когда пользователь касается фигуры и начинает ее перемещение по экрану, фигура заменяется более мелким рисунком. Таким образом, пользователь понимает что фигура захвачена.
  • Я ограничил область перемещения фигуры размерами доски.
  • Если пользователь отпустил фигуру в неправильном месте, она должна вернуться в первоначальное положение.
  • Задачи обозначены, приступим к их реализации.

    Подмена ImageView при касании

    Все мои фигуры представляют собой объекты ImageView. К сожалению, оказалось что реализация Drag & Drop в Android не позволяет "прямо из коробки" осуществлять подмену изображения объекта при его касании. Тем не менее, эта задача вполне решаема средствами API. Нам понадобится выполнить ряд несложных действий:

  • Создать объект DragShadowBuilder.
  • Вызвать метод startDrag.
  • Спрятать наш ImageView, который отображает фигуру, вызвав метод setVisibility с параметром View.INVISIBLE. В результате на экране останется только объект DragShadowBuilder, что будет сигналом пользователю о захвате фигуры.
  • Эти действия необходимо реализовать в обработчике OnTouchListner объекта ImageView. Для этого переопределим метод onTouch:

    @ Override public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent. getAction() == MotionEvent. ACTION_DOWN) { ClipData clipData= ClipData. newPlainText("" , "" ) ; View. DragShadowBuilder dsb= new View. DragShadowBuilder(view) ; view. startDrag(clipData, dsb, view, 0 ) ; view. setVisibility(View. INVISIBLE) ; return true ; } else { return false ; } }

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

    Ограничение области перетаскивания для функции drag drop

    Ограничение области перетаскивания связано с одной проблемой. Дело в том, что если отпустить фигуру за пределами доски, события drop не случится, поскольку пользователь отпустил объект на пустом месте, и объекту не с чем взаимодействовать. В результате фигура не вернется в свое первоначальное состояние и так и останется навсегда спрятанной. Я убил уйму времени на чтение документации, но так и не нашел способа ограничить область перетаскивания объектов. Озарение пришло внезапно. Мне совсем не нужно ограничивать область, мне нужно узнать правильно ли пользователь отпустил фигуру или нет.

    Определение правильности отпускания
    Ответы на свои вопросы я нашел в разделе "handling drag end events" на сайте Android Developers. Вот несколько ключевых моментов:

  • Когда пользователь завершает перетаскивание в обработчике DragListeners генерируется событие ACTION_DRAG_ENDED.
  • В DragListener можно получить более подробную информацию об операции drag, вызвав метод DragEvent.getResult().
  • Если DragListener возвращает true в ответ на событие ACTION_DROP, вызов getResult также вернет true, в противном случае - false.
  • Таким образом, мне нужно перехватить событие ACTION_DRAG_ENDED и вызывать метод getResult. Если он вернет false, значит пользователь утащил фигуру за пределы доски, и мне нужно перевести ImageView в видимый режим.

    @ Override public boolean onDrag(View view, DragEvent dragEvent) { int dragAction= dragEvent. getAction() ; View dragView= (View) dragEvent. getLocalState() ; if (dragAction== DragEvent. ACTION_DRAG_EXITED) { containsDragable= false ; } else if (dragAction== DragEvent. ACTION_DRAG_ENTERED) { containsDragable= true ; } else if (dragAction== DragEvent. ACTION_DRAG_ENDED) { if (dropEventNotHandled(dragEvent) ) { dragView. setVisibility(View. VISIBLE) ; } } else if (dragAction== DragEvent. ACTION_DROP& amp;& amp; containsDragable) { checkForValidMove((ChessBoardSquareLayoutView) view, dragView) ; dragView. setVisibility(View. VISIBLE) ; } return true ; } private boolean dropEventNotHandled(DragEvent dragEvent) { return ! dragEvent. getResult() ; }

    Теперь пользователь может где угодно отпускать фигуру, и ничего страшного не произойдет.

    Определение допустимых ходов

    Последняя часть статьи посвящена проверке допустимости хода, который пытается сделать пользователь. Прежде чем подробно приступить к обсуждению этой темы, сделаю небольшую ремарку, объясняющую структуру моего приложения. Шахматная доска представлена как TableLayout, а каждая клетка является потомком LinearLayout и имеет OnDragListener.

    Кроме того, каждый OnDragListener ссылается на объект "посредника" (mediator), который заботится о взаимодействии игровых объектов и запоминает положение текущей клетки.

    Когда пользователь тащит фигуру над клеткой, возможны следующие действия:

  • Использование события ACTION_DRAG_ENTERED для установки переменной ‘containsDraggable’ в true.
  • Использование события ACTION_DRAG_EXITED для установки переменной ‘containsDraggable’ в false.
  • Использование события ACTION_DROP для запроса посредника о допустимости установки фигуры в эту клетку.
  • Ниже приведен код, реализующий описанную логику

    @ Override public boolean onDrag(View view, DragEvent dragEvent) { int dragAction= dragEvent. getAction() ; View dragView= (View) dragEvent. getLocalState() ; if (dragAction== DragEvent. ACTION_DRAG_EXITED) { containsDragable= false ; } else if (dragAction== DragEvent. ACTION_DRAG_ENTERED) { containsDragable= true ; } else if (dragAction== DragEvent. ACTION_DRAG_ENDED) { if (dropEventNotHandled(dragEvent) ) { dragView. setVisibility(View. VISIBLE) ; } } else if (dragAction== DragEvent. ACTION_DROP& amp;& amp; containsDragable) { checkForValidMove((ChessBoardSquareLayoutView) view, dragView) ; dragView. setVisibility(View. VISIBLE) ; } return true ; }

    Как видите, не зависимо от того допустим ли ход или нет, ImageView переводится в видимое состояние. Я хотел, чтобы пользователь видел, как перемещается фигура. Ранее я упоминал, что клетка является потомком LayoutView. Это сделано для того чтобы проще перемещать ImageView от клетки к клетке. Ниже приводится код метода checkForValidMove, который показывает, как происходит перемещение ImageView.

    private void checkForValidMove(ChessBoardSquareLayoutView view, View dragView) { if (mediator. isValidMove(view) ) { ViewGroup owner= (ViewGroup) dragView. getParent() ; owner. removeView(dragView) ; view. addView(dragView) ; view. setGravity(Gravity. CENTER) ; view. showAsLanded() ; mediator. handleMove(view) ; } }

    Надеюсь, эта статья поможет Вам при разработке собственных проектов.

    
    Top