(Я в настоящее время использую версию 4.0 SDK, но я изначально написал код в этой серии с версией 3.1.)
Зачем использовать Drag and Drop
Когда я начал работать над своим Android-приложением, которое включает в себя перемещение шахматной фигуры по шахматной доске, я решил использовать функцию перетаскивания по двум причинам:
- Я чувствовал, что перетаскивание максимально приблизится к опыту перемещения фигуры по доске.
- Используя перетаскивание, будет намного легче определить действительные ходы, опуская шахматную фигуру на отдельный объект (в данном случае квадрат на шахматной доске), а не отслеживая координаты x, y шахматной фигуры. Я объясню больше о том, как я реализовал это в последней части этой серии
Мои цели перетаскивания
У меня было 3 конкретные цели, когда речь шла о реализации перетаскивания в отношении перемещения шахматной фигуры на доске.
- Когда пользователь нажимает и начинает перетаскивать, шахматная фигура будет заменена гораздо более легкой версией оригинала, делая очевидным, что она перемещается.
- Я хотел ограничить область, куда можно перетащить шахматную фигуру, а точнее, я не хочу, чтобы игроки перетаскивали фигуру с доски
- Если игрок попытается сбросить игровую фигуру, что приведет к незаконному ходу, я бы хотел, чтобы игровая фигура «вернулась» в исходное положение.
Здесь я расскажу о своей первой цели — перетаскивать в моем приложении.
Изменение ImageView на Drag Start
Когда я начал работать, я понял, что функциональность перетаскивания не поддерживает то, что я хотел прямо из коробки, но, конечно, после небольшой работы в API было достаточно, чтобы дать мне то, что я искал. Когда пользователь нажимает на шахматную фигуру (которая является ImageView), чтобы начать перетаскивание, вот шаги, которые я делаю:
- Создайте объект DragShadowBuilder.
- Вызовите метод startDrag.
- Скройте исходный ImageView (шахматная фигура), вызвав setVisibility и передав в View.INVISIBLE, оставив видимым только объект DragShadowBuilder, ясно указывающий, что перетаскивание началось.
Все вышеперечисленные шаги выполняются в объекте OnTouchListner объекта ImageView в реализованном методе onTouch. Вот код:
01
02
03
04
05
06
07
08
09
10
11
12
|
@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 ; } } |
Изменено изображение шахматной фигуры при начале перетаскивания!
До сих пор я рассмотрел, как скрыть ImageView и только что показал объект DragShadowBuilder в начале перетаскивания. Теперь я собираюсь сосредоточиться на ограничении области перетаскивания.
Попытка ограничить область перетаскивания
Поскольку мое приложение предполагает перемещение шахматной фигуры по доске, я хотел ограничить область перетаскивания самой игровой доской. Моя мотивация заключается в том, что ImageView скрывается в начале перетаскивания. Если ImageView упал с доски, он никогда не появится снова. Это оставило бы мое заявление в несогласованном состоянии. Поэтому я потратил некоторое время на просмотр API Android и на поиски, как я могу ограничить область перетаскивания. Я ничего не придумал. Затем я сделал шаг назад и подумал, какую проблему я пытаюсь решить? Это пользователь перетаскивает ImageView с доски? Нет, настоящая проблема в том, что действие перетаскивания никогда не обрабатывается, поэтому у меня никогда не будет возможности снова сделать ImageView видимым. Поэтому я пересмотрел свою проблему, чтобы определить, является ли падение допустимым или нет.
Определение допустимых капель
Чтобы выяснить, как определить допустимые отбрасывания, я вернулся к документации разработчика Android. Я заточил в обработке событий торможения . Вот ключевые моменты, которые я нашел:
- Когда перетаскивание заканчивается, событие ACTION_DRAG_ENDED отправляется всем DragListeners.
- DragListener может определить дополнительную информацию об операциях перетаскивания, вызвав метод getResult объекта DragEvent.
- Если DragListener вернул true из события ACTION_DROP, вызов getResult вернет true, иначе false.
Теперь мне совершенно ясно, что я могу сделать, когда / если пользователь перетаскивает шахматную фигуру в непредусмотренной области. Мой класс DragListener уже возвращает true для любого события, которое обрабатывается в методе onDrag. Таким образом, возвращаемое значение false из getResult указывает, что падение произошло где-то за пределами игрового поля. Что мне нужно сделать сейчас:
- Когда происходит событие ACTION_DRAG_ENDED, вызовите метод getResult
- Если getResult возвращает false, немедленно установите видимость ImageView (используется в начале перетаскивания) обратно на видимость.
Вот код, соответствующие строки выделены:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@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 && containsDragable) { checkForValidMove((ChessBoardSquareLayoutView) view, dragView); dragView.setVisibility(View.VISIBLE); } return true ; } private boolean dropEventNotHandled(DragEvent dragEvent) { return !dragEvent.getResult(); } |
Теперь пользователи могут перетаскивать ImageView куда угодно, и игра не окажется в несовместимом состоянии. Моя вторая цель достигнута.
В последней части рассказывается о перемещении игровой фигуры в результате действительного хода или о том, что эта игровая фигура «вернулась» в исходное положение, когда был сделан неверный ход.
Информация о приложении
Для этого поста может быть полезна некоторая справочная информация о моем заявлении. Игра включает в себя перемещение одной шахматной фигуры (рыцаря) вокруг шахматной доски. Шахматная доска является TableLayout, и каждый квадрат является подклассом LinearLayout и имеет набор OnDragListener. Кроме того, каждый OnDragListener имеет ссылку на объект-посредник, который:
- Управляет связью между объектами.
- Отслеживает текущую клетку (клетку посадки последнего действующего хода).
Определение допустимых ходов
Когда пользователь начинает перетаскивать ImageView и проходит через данный квадрат, возможны следующие действия:
- Используйте событие ACTION_DRAG_ENTERED, чтобы установить для переменной экземпляра ‘containsDraggable’ значение true.
- Используйте событие ACTION_DRAG_EXITED, чтобы установить для «containsDraggable» значение false.
- Когда событие ACTION_DROP происходит в квадрате, посреднику задается вопрос, является ли попытка перемещения допустимой, путем проверки всех возможных допустимых перемещений (предварительно рассчитанных) из «текущего» квадрата.
Вот код с выделенными соответствующими разделами:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
@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 && containsDragable) { checkForValidMove((ChessBoardSquareLayoutView) view, dragView); dragView.setVisibility(View.VISIBLE); } return true ; } |
Как видно из вышеизложенного, независимо от того, является ли перемещение действительным или нет, ImageView становится видимым. Сделав ImageView видимым сразу после удаления, мои цели плавного перемещения или «возврата» к исходному положению достигнуты.
Перемещение ImageView
Ранее я упоминал, что каждый квадрат является подклассом LayoutView. Это было сделано для облегчения перемещения ImageView от квадрата к квадрату. Вот подробности из метода checkForValidMove (строка 14 в примере кода выше), которые показывают, как выполняется перемещение ImageView.
01
02
03
04
05
06
07
08
09
10
|
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); } } |
Я надеюсь, что читатель найдет эту статью полезной в своих собственных усилиях по разработке Android.
Ссылка: Android Drag and Drop (Часть 1) , Android Drag and Drop (Часть 2) , Android Drag and Drop (Часть 3) от нашего партнера по JCG Билла Бецеката из блога « Случайные мысли о кодировании» .
Статьи по Теме :