(我目前使用的是sdk的4.0版,但我最初使用3.1版编写了该系列的代码。)
为什么要使用拖放
当我开始处理涉及在棋盘周围移动棋子的Android应用程序时,出于以下两个原因,我选择使用拖放功能:
- 我觉得拖放将尽可能接近将棋子在板上移动的体验。
- 通过拖放,通过将棋子放在单个对象(在本例中为棋盘上的正方形)上,而不是跟踪棋子的x,y坐标,可以更有效地确定有效棋步。 我将在本系列的最后一部分中解释有关如何实现此功能的更多信息。
我的拖放目标
在实现拖放方面,在棋盘上移动棋子方面,我有3个具体目标。
- 当用户按下并开始拖动时,棋子将替换为原版的轻得多的版本,从而很明显它已被移动。
- 我想限制可拖动棋子的区域,更具体地说,我不想让玩家将棋子从棋盘上拖出
- 如果玩家试图放下游戏棋子而导致非法移动,我希望游戏棋子“弹回”到其原始位置。
在这里,我将介绍我拖放应用程序的第一个目标。
在开始拖动时更改ImageView
当我开始工作时,我意识到拖放功能并不能立即支持我想要的功能,但是可以肯定的是,只需少量的工作,api中的功能就足以满足我的需求。 当用户按下棋子(即ImageView)以开始拖动时,我将采取以下步骤:
- 创建一个DragShadowBuilder对象。
- 调用startDrag方法。
- 通过调用setVisibility并传递View.INVISIBLE来隐藏原始的ImageView(象棋棋子),仅使DragShadowBuilder对象可见,从而清楚地指示拖动已开始。
上面的步骤全部在ImageView的OnTouchListner对象中的已实现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;
}
}
更改了开始拖动时的棋子图像!
到目前为止,我已经介绍了如何隐藏ImageView并仅在拖动开始时显示了DragShadowBuilder对象。 现在,我将集中精力限制拖动区域。
试图限制拖动区域
由于我的应用程序涉及在棋盘上移动棋子,因此我想将拖动区域限制在游戏板本身上。 我的动机是在拖动开始时将ImageView隐藏。 如果ImageView从板上掉下来,它将再也不会出现。 那会使我的应用程序处于不一致状态。 因此,我花了一些时间浏览Android api,并仔细研究了如何限制拖动区域。 我什么都没想到。 然后我退后一步,以为我要解决什么问题? 用户是否将ImageView拖到板上? 不,真正的问题是放置动作从未处理过,所以我再也没有机会使ImageView再次可见。 因此,我将问题重新定义为确定放置有效期的时间。
确定有效丢弃
为了弄清楚如何确定有效的放置,我返回了Android Developer文档。 我在处理拖曳结束事件方面做得很好。 这是我发现的要点:
- 拖动结束后,将ACTION_DRAG_ENDED事件发送到所有DragListener。
- DragListener可以通过调用DragEvent的getResult方法来确定有关拖动操作的更多信息。
- 如果DragListener从ACTION_DROP事件返回true,则getResult调用将返回true,否则返回false。
现在我很清楚,当/如果用户将棋子拖放到非预期区域时,我可以做什么。 对于onDrag方法中处理的任何事件,我的DragListener类都已经返回true。 因此,从getResult返回的false值表示该掉落发生在游戏板上某处。 我现在需要做的是:
- 发生ACTION_DRAG_ENDED事件时,请调用getResult方法
- 如果getResult返回false,请立即将ImageView的可见性(在拖动开始时使用)重新设置为visible。
这是代码,相关行突出显示:
@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事件时,通过从“当前”正方形检查所有可能的有效移动(预先计算),询问调解员尝试的移动是否有效。
以下是突出显示相关部分的代码:
@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的移动。
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开发工作有所帮助。
参考:来自我们JCG合作伙伴 Bill Bejeckat的《 随机编码想法》博客中的Android拖放(第1部分) , Android拖放(第2部分) , Android拖放(第3部分) 。
相关文章 :
翻译自: https://www.javacodegeeks.com/2011/12/android-drag-and-drop-tutorial.html