(2013年12月)[Android]ドラッグ&ドロップで入れ替え可能なGridViewを作る(書きかけ)

SDKには用意されていないから自分で実装しなければならない。なんて面倒なんだ…

以下、自分用の備忘録なので文章は雑です。(しかも書きかけ…ぼちぼち更新します)

仕様

こんな感じにしたい。

  • Itemをロングタップすると選択状態になり、Itemが少し拡大される
  • 選択状態のItemはドラッグできる。ただし自動スクロールは実装しない
  • ドロップすると、その位置に一番近いマスに配置される(縮小アニメーションで元のサイズに戻りながら)。このとき、空いたマスを詰めるために、それ以降のItemが順番を保持しながら1つ前のpositionに移動する(並べ替え)。この並べ替えはアニメーションで。
  • ドラッグ中も並べ替えアニメーションをやる

実装上のポイント

  • touch eventの取得
  • GridViewにセットしているデータの並べ替え処理
  • アニメーション

実装例を調査

ググるといろんな人がいろいろ努力した結果が出てきて涙ぐましいよ。 でもいろんな人がいろいろな方法で実装してるから混乱してしまった… そんなわけでちょっとまとめてみようと思う。

Drag-Drop for an Android GridView | More Is Not Always Better

下記のPart1~3でドラッグ&ドロップの基礎部分を解説し、この記事ではそれらをGridViewに組み込んだコードを紹介している。ソート機能は無い。

Moving Views in Android – Part 1 | More Is Not Always Better

Moving Views In Android – Part 2, Drag and Drop | More Is Not Always Better

Moving Views In Android – Part 3, Drop Zones | More Is Not Always Better

Drag-Drop for Android GridView (V4) | More Is Not Always Better

上の記事のドラッグ&ドロップ部分をAndroid support v4版に書き換えたもの…?

Androidでドラッグアンドドロップ (フェンリル | デベロッパーズブログ)

2012年4月の記事なので、Android3.x系うんぬんの話はちょっと古い。ソースコードの概要部分のみしか示されていないので、実装部分は自分で調べて書く必要あり。ただ、ドラッグ&ドロップでソート可能なGridViewはどうすれば実現できるかおおまかに把握できる良い記事。

  • タッチイベント

Android3.x系の例としてOnDragListenerでドラッグ&ドロップを実現している。 Android2.x系ではonLongClickとonTouchを使ってドラッグ&ドロップが実現できることを紹介。

  • データのソート

mContainer.addView(mDragView, pos);の部分がViewの並べ替えに相当するんだけど、mContainerが何のクラスなのかわからない。

  • アニメーション

親のViewGroupにLayoutTransitionをセットすることでアニメーションさせている。

visible true: 【Android2.2以上】 GridView,ListViewでドラッグ&ドロップ、自動スクロールもする。動画もあるよ。

AbsListViewならなんでも(GridViewもListViewも)対応可能というビックリな例。その発想は無かった!ただ、ドラッグ&ドロップ部分とソート部分をLinearLayoutの子クラスに実装しているのがちょっと気持ち悪いかも。

  • タッチイベント

LinearLayoutの子クラスSortableListViewLayout(GridViewを子Viewに持つ)にonItemLongClickとonInterceptTouchEventのリスナーをセット。onInterceptTouchEventについては次の記事がわかりやすい。

Androidあれこれ: 子Viewのタッチイベントを取得する

"子Viewの実装の有無に関わらず、親となるViewGroupでタッチイベントを取得したい時ってありますよね。 そんな時に便利なのがViewGroup#onInterceptTouchEventというメソッド。"

  • データのソート

SortableListViewLayoutにAdapterもデータも保持していて、ドロップ時にデータを直接入れ替えて、AbsListView.invalidateViews()でデータ更新を画面表示に反映させる。

  • アニメーション

ドラッグ開始時に選択したItemのゴーストを作成し、そのゴーストをドラッグさせるタイプ。ゴーストはImageViewで、選択したItemのDrawingCacheをセットする。WindowManager.addViewでゴーストをSortableListViewLayoutに追加し、WindowManager.LayoutParamsのx,y座標を弄ってWindowManager.updateViewLayoutでゴーストの配置を更新する。

GridViewなどのAbsListViewの項目をドラッグ&ドロップで入れ替える - JDBな人生

mrKlar/PagedDragDropGrid · GitHub

複数ページを切り替えられる機能は今回は必要ないので読み飛ばす。ドラッグ&ドロップの動作は遅延も変な挙動も無く、完璧でとても参考になる。

ただ、ページ切り替え機能を実装したり、GridをViewGroupのサブクラスで作り直して、アダプターのセットやビューの追加・配置まで実装したりしているためか、コードがとても複雑。

  • タッチイベント

ViewGroupを継承したDragDropGridのコンストラクタはOnTouchListenerとOnLongClickListenerを自身にセットする。onTouchでACTION_DOWN,MOVE,UPを、onLongClickでロングタップを検知する。ACTION_UPではドロップ後の処理だけでなく、アイテムがタップされた場合にOnClickListenerのonClickを呼び出す処理もする。

  • データのソート

ポジション情報をDragDropGrid内のSparseIntArrayに保存しているっぽい。これは、TranslateAnimationではViewの実体は移動しないため、ドラッグ中にどのItemがどこのマスにあるのかGridView側で把握していなきゃならないから、かな。アイテムデータ自体の入れ替えはAdapterのswapItemsメソッド(独自interfaceで定義)で行う。

  • アニメーション

選択時の拡大アニメーションはScaleAnimationをViewに適用。移動時はACTION_MOVEが呼ばれるたびに選択状態のViewのlayoutメソッドを呼び出して、指の位置に再配置する。入れ替え対象のViewはTranslateAnimationで移動アニメーションを実現する。ドロップ時はView.clearAnimationでアニメーションをキャンセルするだけ。

ドラッグ&ドロップで並べ替え可能なListViewの実装例

今回はGridViewだけど、ListViewも兄弟なので同じようなもん。

2010-07-18 - @vvakame の日記

日本語記事で初出なのかな?偉大なる先人ですね。

ユーザがソート可能なListViewをすこしリッチにしてみた - 明日の鍵

ドラッグ&ドロップで並び替えできる ListView - パンダのメモ帳

上2つの記事をもとにしたコードだそうな。

Android Developersで参考になるページ

やっぱり公式ドキュメントが一番頼りになるよね。英語だけど。

その他に参考になりそうな記事

Androidでドラッグ・アンド・ドロップ - hidecheckの日記

"Canvasを使わないでドラッグアンドドロップする方法"

Drag and Drop を使う

"まず、上下に2つの GridView ベースのカスタムビューを用意します。その中に予め5つのアイテムを配置します。これらのアイテムは Drag and Drop で上下の View を行き来させることができます。"

Android開発のメモ: 12月 2010

"座標系について"

ドラッグ&ドロップで入れ替え可能なGridViewを作ってみた

// まだ作ってない…