android3.0から、startDrop()メソッドをViewにセットすることで、容易にドラッグ&ドロップできるようになりました。
今回、TextViewをドラッグ可能にカスタムし、ボタン上をドラッグさせ、最後にボタン上にドロップすることで、Buttonの色を変化させるサンプルについて説明します。
サンプルは、わかりやすいようにシンプルに記述しております。
※現在公開されているHoneycombのSDKはプレビュー版の為、正式版では実装が異なる可能性があります。
それでは続きをどうぞ、、、
TextViewをドラッグ可能にカスタムする
まず、TextViewをドラッグできるようにカスタムします。今回は、TextViewを長押しすることで、ドラッグを開始するようにします。
onLongClick()メソッドが呼び出された際に、startDrag()メソッドをドラッグしたいViewにセットし、ドラッグを開始します。
public class DraggableView extends TextView { public DraggableView(Context context) { super(context); setOnLongClickListener(new View.OnLongClickListener() { public boolean onLongClick(View v) { ClipData data = ClipData.newPlainText("text", "text : " + v.toString()); v.startDrag(data, new DragShadowBuilder(v),(Object)v, 0); return true; } }); } }
注目すべきは、onLongClick()メソッド内での動作です。今回は、TextView長押しでドラッグを始めたいので、onLongClick()メソッド内にて、自分自身に対してstartDrag()メソッドをセットしています。startDragメソッドの引数は、以下の4つです。
第1引数 ClipData data |
Dragを表す情報を持ったClipData ※ |
第2引数 View.DragShadowBuilder shadowBuilder |
Drag中の影を表示させる |
第3引数 Object myLocalState |
最も最近のドラッグイベントによって渡されるオブジェクト |
第4引数 int flags |
エフェクトフラグ。特に指定をしない場合は「0」で可。 |
第2引数でDragShadowBuilderのインスタンスを生成し、引数にviewを指定することで、引数に指定したviewのドラッグ中に影が表示され、見た目上にもドラッグされているように振る舞います。
※ClipDataクラスについては、また別の機会に詳しく説明したいと思います。
これで、TextViewを長押し後、ドラッグが可能になります。
ドラッグ開始〜終了(ドロップ)までの各種イベントを受け取る
次に、カスタムTextView内で、ドラッグ中のイベントを受け取ります。
イベントを受け取るメソッドは、onDragEvent()です。このメソッド内で、各種イベント取得時の動作を行います。
今回は、各種イベント取得時に、LogCatにLogを吐き出すようにしています。
@Override public boolean onDragEvent(DragEvent event) { boolean result = false; switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: { //ドラッグ開始時に呼び出し Log.i("DragSample", "Drag started, event=" + event); result = true; } break; case DragEvent.ACTION_DRAG_ENDED: { //ドラッグ終了時に呼び出し Log.i("DragSample", "Drag ended."); } break; case DragEvent.ACTION_DRAG_LOCATION: { //ドラッグ中に呼び出し Log.i("DragSample", "... seeing drag locations ..."); result = true; } break; case DragEvent.ACTION_DROP: { //ドロップ時に呼び出し Log.i("DragSample", "Got a drop! =" + this + " event=" + event); result = true; } break; case DragEvent.ACTION_DRAG_ENTERED: { //ドラッグ開始直後に呼び出し Log.i("DragSample", "Entered " + this); result = true; } break; case DragEvent.ACTION_DRAG_EXITED: { //ドラッグ終了直前に呼び出し Log.i("DragSample", "Exited " + this); result = true; } break; default: Log.i("DragSample", "other drag event: " + event); result = true; break; }
以上のように記述することで、各種イベント発生時の処理を記述することが可能です。
ここで注意しなければならないのは、各種イベント発生後に、イベントが正常に完了したことを通知するために、戻り値としてtrueをセットする必要があります。
それをしないと、イベントがエラーとして通知されているため、その後のイベントが正常に発生しません。例えば、ACTION_DRAG_STARTEDイベント発生時の処理の後、falseを返すと、その後ドロップ終了のACTION_DRAG_ENDEDのイベントが発生するまで、他のイベントはLog上に吐き出されませんでした。
通常時のLogの順番•••STARTED→ENTERED→LOCATION→EXITED→ENDED
STARTEDイベント処理時にfalseをreturnした場合のLog•••STARTED→ENDED
ドラッグ•ドロップを受ける側のviewの処理
今回、Activity内にボタンを3つ配置し、TextViewが下から順にドラッグされていくと、下の2つのボタンはドラッグに反応して青色に変化し、最後に一番上のボタンはドロップに反応して赤色に変化させます。
それぞれのボタンが、それぞれのボタン上で、ドラッグ中のイベントが発生していることを検知するようにします。
これを実現するには、それぞれのボタンに、setOnDragListener()をセットし、onDrag()メソッドを呼び出します。
btn1.setOnDragListener(new View.OnDragListener() { public boolean onDrag(View v, DragEvent event) { final int action = event.getAction(); boolean result = false; switch (action) { case DragEvent.ACTION_DRAG_STARTED: { draggableText.setVisibility(View.VISIBLE); result = true; } break; case DragEvent.ACTION_DRAG_ENDED: { Log.i("DragSample", "Drag ended."); } break; case DragEvent.ACTION_DRAG_LOCATION: { result = true; }break; case DragEvent.ACTION_DROP:{ //ドロップ時にボタンの色を赤色に変化 btn1.setBackgroundColor(Color.RED); result = true; }break; } return result; } });
少し長くなるので、ここでは最上段のボタンのみ例に取って説明します。
btn1(最上段のボタンのインスタンス)に対してsetOnDragListenerをセットしてドラッグイベントを検知し、onDrag()メソッドを呼び出します。
event.getAction()にてイベントの種類を受け取り、switch構文にてそれぞれのイベント発生時の処理を分岐します。
上記コードの最上段のボタンは、ドロップ時にボタンの色を赤色に変化させたいので、ACTION_DROPイベント発生時の処理の中で、setBackgroundColor()メソッドによって、背景色を変化させています。
下2段のボタンは、ドラッグ時に青色に変化させたいので、CTION_DRAG_LOCATIONイベント発生時の処理の中で、同様に背景色を青色に変化させます。詳細な実装はサンプルコードを参考にしてみてください。
最後に、アプリの動作中のキャプチャ画像を添付して終わります。