Недавно у меня появилась идея заняться разработкойигры для андроид. Для начала я решил написать шахматы. Мне казалось технологияDrag and Drop отлично подойдет для реализации механизма перемещения фигур. Для непосвященных отмечу, чтометод drag and drop заключается в возможности перетаскивания одних графических объектов на другие и выполнения того или иного действия после отпускания. Простейший пример - удаление ярлыка с рабочего стола вашего ПК перетаскиванием его в корзину. "Кинув" ярлык в корзину, мы говорим системе, что хотим заставить взаимодействовать эти два объекта. Система получает наш сигнал и решает, какое действие ей стоит предпринять. Drag and drop получила широкое распространение благодаря своей интуитивной ясности. Этот подход подкреплен нашим опытом взаимодействия с объектами реального мира и прекрасно работает в виртуальной среде. Что же касается шахмат, с помощью drag and drop технологически проще определить клетку, куда пользователь перетащил фигуру, поскольку не нужно вычислять номер клетки по координатам точки отпускания. Эту работу возьмет на себя виртуальная машина.
Использование технологии drag and drop позволяет мне малой кровью решить три задачи:
Задачи обозначены, приступим к их реализации.
Все мои фигуры представляют собой объекты ImageView. К сожалению, оказалось что реализация Drag & Drop в Android не позволяет "прямо из коробки" осуществлять подмену изображения объекта при его касании. Тем не менее, эта задача вполне решаема средствами API. Нам понадобится выполнить ряд несложных действий:
Эти действия необходимо реализовать в обработчике 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; } }
Все очень просто. Итак, с подменой изображения разобрались, перейдем к следующей задаче.
Ограничение области перетаскивания связано с одной проблемой. Дело в том, что если отпустить фигуру за пределами доски, события drop не случится, поскольку пользователь отпустил объект на пустом месте, и объекту не с чем взаимодействовать. В результате фигура не вернется в свое первоначальное состояние и так и останется навсегда спрятанной. Я убил уйму времени на чтение документации, но так и не нашел способа ограничить область перетаскивания объектов. Озарение пришло внезапно. Мне совсем не нужно ограничивать область, мне нужно узнать правильно ли пользователь отпустил фигуру или нет.
Определение правильности отпускания
Ответы на свои вопросы я нашел в разделе "handling drag end events" на сайте Android Developers. Вот несколько ключевых моментов:
Таким образом, мне нужно перехватить событие 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), который заботится о взаимодействии игровых объектов и запоминает положение текущей клетки.
Когда пользователь тащит фигуру над клеткой, возможны следующие действия:
Ниже приведен код, реализующий описанную логику
@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"
Перевод:Александр Ледков