2024年6月29日土曜日

Android RecyclerView コンテンツをドラッグ&ドロップする実装を解説

どうも。どっことです。今回はRecyclerViewを使ったコンテンツのドラッグ&ドロップの実装方法について解説します。

ドラッグ&ドロップ機能を持ったRecyclerView

RecyclerViewの一番の利点は、拡張性に富んでいることだと思います。RecyclerViewが登場する以前では、ListViewがシンプルにコンテンツ一覧を表示する機能を担っていました。実装難易度もそこまで高くなく、単に一覧表示をさせるものであれば、今でもこれで十分です。しかし、より便利に、より良いUXを提供することを考えるとListViewではもうまかないきれないです。RecyclerViewは、拡張しやすいコンポーネントとして出てきたもので、今回のような機能はまさにRecyclerViewが活きるパターンの一つだといえます。

RecyclerViewにドラッグ&ドロップを実装する

それでは実装手順を解説します。

  1. ItemTouchHelperインスタンスを実装する。
  2. ItemTouchHelperRecyclerViewにアタッチする。

順番に解説していきます。

ItemTouchHelperインスタンスを実装する

まずはItemTouchHelperを実装します。

ItemTouchHelperは、今回のドラッグ&ドロップをはじめとするユーザ操作を司るヘルパークラスとなっています。今回のドラッグ&ドロップでは、SimpleCallBack#onMove,SimpleCallBack#onMoved, SimpleCallBack#isLongPressDragEnabledのコールバックに追加実装が必要となってきます。

早速サンプルを載せます。

ItemTouchHelper(object : SimpleCallback(UP or DOWN, 0) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        // 特定のコンテンツがドラッグされようとしている 
        val fromPosition = viewHolder.adapterPosition
        val toPosition = target.adapterPosition
        recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)
        // ドラッグしてOKなら true を, NGなら false を返す
        return true
    }

    override fun isLongPressDragEnabled(): Boolean {
        // 長押しされた。ドラッグ&ドロップを有効にする
        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        // 呼ばれない
    }

    override fun onMoved(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        fromPos: Int,
        target: RecyclerView.ViewHolder,
        toPos: Int,
        x: Int,
        y: Int
    ) {
        super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
        // コンテンツがドラッグ&ドロップされた。
    }
})

まずSimpleCallBack#isLongPressDragEnabledでは、ユーザがコンテンツを長押しした時にドラッグ&ドロップを開始するかどうかを返します。trueなら開始する、falseならしないということになります。今回は簡単のため、trueを返すようにしています。

onMoveコールバックでは、ユーザのドラッグによりコンテンツをドラッグさせても問題ないかをチェックします。同じRecyclerView内に複数の種類のコンテンツが表示されている場合=並び替えをさせたくない場合はここでfalseを返すことで、そのドラッグ&ドロップを無効にすることができます。

そしてonMovedコールバックは、実際にコンテンツのドラッグ&ドロップが行われたタイミングでここの処理が呼び出されます。このタイミングで、コンテンツの要素を渡された引数の内容をもとに更新してあげるのがよいでしょう。

ItemTouchHelperをRecyclerViewにアタッチする

あとは実装したItemTouchHelperRecyclerViewにアタッチするだけです。Activity#onCreatedFragment#onViewCreatedあたりで以下を読んであげれば問題ありません。

itemTouchHelper.attachToRecyclerView(recyclerView)

まとめ

今回はRecyclerViewを使ったコンテンツのドラッグ&ドロップの実装方法について解説しました。RecyclerViewは拡張性が高く、今回以外の拡張も豊富に取り揃えているので、今後もテーマにしていきたいと思います。

参考

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。