【Android】ドラッグ&ドロップの実装

#概要
アプリ制作をしている中で、Viewをドラッグ&ドロップしたい状況があると思います。
今回はViewのドラッグ&ドロップを実現する方法を紹介します。

##開発環境
- AndroidStudio：1.5.1
- JDK：1.7.0
- Android：5.0.2 (Lollipop)

#手順
##1.layoutファイルにViewを配置
まず初めに、layoutファイルにViewを配置します。
Viewクラスを継承したものなら何でも構いませんが、
今回はTextViewを用いて説明いたします。

```xml:activity_main.xml
<!-- ドラッグ&ドロップするView -->
<TextView
    android:id="@+id/dragged_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    android:textSize="50sp"/>
```
![device-2016-08-11-120043.png](https://qiita-image-store.s3.amazonaws.com/0/136368/c3a8b32c-5f7a-de81-cb63-64ac2d4af0e4.png)
##2.長押しの実装
上記のTextViewを長押しをした際に、ドラッグ&ドロップが出来るようにします。
ここからはコード上での処理になります。

1.まずはMainActivityにOnLongClickListenerを実装します。

```java:MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnLongClickListener {

...

    /* OnLongClickListenerを実装したViewが長押しされた際に呼び出されるメソッド */
    @Override
    public boolean onLongClick(View v) {
    /* ここに長押しした際の処理を記述します */
        return true;
    }
```

2.続いて、layoutファイルからドラッグ&ドロップしたいTextViewを取得します。

```java:MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnLongClickListener {
    /* 先ほどのTextViewをメンバ変数で保持 */
    private View mDraggedView;

    /* 画面起動前のViewの初期化処理などを行うメソッド */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* layoutファイルからTextViewを取得 */
        mDraggedView = findViewById(R.id.dragged_view);
        /* OnLongClickListenerをTextViewにセット */
        mDraggedView.setOnLongClickListener(this);
    }
```

3.全体のコードはこのようになります。

```java:MainActivity.java
/* View.OnLongClickListenerの実装 */
public class MainActivity extends AppCompatActivity implements View.OnLongClickListener {
    /* 先ほどのTextViewをメンバ変数で保持 */
    private View mDraggedView;

    /* 画面起動前のViewの初期化処理などを行うメソッド */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* layoutファイルからTextViewを取得 */
        mDraggedView = findViewById(R.id.dragged_view);
        /* OnLongClickListenerをTextViewにセット */
        mDraggedView.setOnLongClickListener(this);
    }

    /* OnLongClickListenerを実装したViewが長押しされた際に呼び出されるメソッド */
    @Override
    public boolean onLongClick(View v) {
    /* ここに長押しした際の処理を記述します */
        return true;
    }
}
```

##3.ドラッグ&ドロップの開始
取得したTextViewをドラッグ&ドロップするには`startDrag()`メソッドを使用します。
先程記述した`onLongClick()`内で`startDrag()`メソッドを実行します。
コードは以下のようになります。

```java:MainActivity.java
/* OnLongClickListenerを実装したViewが長押しされた際に呼び出されるメソッド */
@Override
public boolean onLongClick(View v) {
/* ここに長押しした際の処理を記述します */

    /* ドラッグ&ドロップ処理を開始する */
    v.startDrag(null, new View.DragShadowBuilder(v), v, 0);

    return true;
}
```

これでひとまずViewをドラッグすることができます。
![device-2016-08-11-131004.png](https://qiita-image-store.s3.amazonaws.com/0/136368/cde954d9-1334-2c58-f91b-08f838f4a755.png)

##`startDrag()`メソッドに渡されている4つの引数について
**1.ClipData data**
startDrag()メソッドにClipDataを渡すことで、
ClipDataに保持させたデータをドロップ処理の際に取り出すことができます。
ClipDataには文字列やIntentなどを保持させることができます。
今回はデータの受け渡しをする必要がないのでnullを指定しています。

**2.DragShadowBuilder shadowBuilder**
DragShadowBuilderとは、その名の通りドラッグの際の影を生成するクラスです。
今回の例で言うと、長押し時にHello,Worldと書いてあるTextViewの薄い分身を生成し、
その分身をドラッグで動かすイメージになります。
この分身を生成してくれるのがDragShadowBuilderになります。
今回は特に細かい指定はしていませんが、この影の大きさやドラッグした際の指との位置関係などをカスタムすることも可能です。

**3.Object myLocalState**
startDrag()メソッドの3つ目の引数でViewを渡すと、ドラッグ&ドロップ処理の通知を受け取るメソッド内でLocalState(ドラッグされたView)として、そのView自信をObjectで受け取ることができます。
ドラッグされてきたViewに対して何か操作を行いたい場合に有効です。

**4.int flags**
これはよく分かりません(笑)
Viewクラスのソースを見た限り、0を渡しておけば問題ないようです。

#おわりに
今回はViewのドラッグ&ドロップの実装方法について解説しました。
次回はドラッグ後の処理やドロップ時の処理を解説したいと考えています。

分かりづらい点や記事・コードに関するご指摘などありましたら、
気軽にコメントしていただけると助かります。

最後に、この記事を読んでいただき誠にありがとうございます。
以上