Технология Drag and Drop в Android

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

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

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

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


Задачи обозначены, приступим к их реализации.

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


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

  1. Создать объект  DragShadowBuilder.
  2. Вызвать метод startDrag.
  3. Спрятать наш 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);
returntrue;
}else{
returnfalse;
}
}

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

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

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

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

  1. Когда пользователь завершает перетаскивание в обработчике DragListeners генерируется событие  ACTION_DRAG_ENDED.
  2. В DragListener можно получить более подробную информацию об операции drag, вызвав метод DragEvent.getResult().
  3. Если 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;
}elseif(dragAction== DragEvent.ACTION_DRAG_ENTERED){
containsDragable=true;
}elseif(dragAction== DragEvent.ACTION_DRAG_ENDED){
if(dropEventNotHandled(dragEvent)){
dragView.setVisibility(View.VISIBLE);
}
}elseif(dragAction== DragEvent.ACTION_DROP&& containsDragable){
checkForValidMove((ChessBoardSquareLayoutView) view, dragView);
dragView.setVisibility(View.VISIBLE);
}
returntrue;
}
 
private boolean dropEventNotHandled(DragEvent dragEvent){
return!dragEvent.getResult();
}

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

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


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

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

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

  1. Использование события ACTION_DRAG_ENTERED для установки переменной  ‘containsDraggable’ в true.
  2. Использование события ACTION_DRAG_EXITED  для установки переменной  ‘containsDraggable’ в false.
  3. Использование события 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;
}elseif(dragAction== DragEvent.ACTION_DRAG_ENTERED){
containsDragable=true;
}elseif(dragAction== DragEvent.ACTION_DRAG_ENDED){
if(dropEventNotHandled(dragEvent)){
dragView.setVisibility(View.VISIBLE);
}
}elseif(dragAction== DragEvent.ACTION_DROP&& containsDragable){
checkForValidMove((ChessBoardSquareLayoutView) view, dragView);
dragView.setVisibility(View.VISIBLE);
}
returntrue;
}

Как видите, не зависимо от того допустим ли ход или нет, 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);
}
}

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



Источник:  Bill Bejeckat "Android Drag and Drop"
Перевод:Александр Ледков




Наши соцсети

Подписаться Facebook Подписаться Вконтакте Подписаться Twitter Подписаться Google Подписаться Telegram

Популярное

Ссылки

Новости [1] [2] [3]... Android/ iOS/ J2ME[1] [2] [3]) Android / Архив

Рейтинг@Mail.ru Яндекс.Метрика
MobiLab.ru © 2005-2018
При использовании материалов сайта ссылка на www.mobilab.ru обязательна