【Kotlin/Android Studio】ItemTouchHelperの使い方!RecyclerViewでスワイプ処理を実装する方法

この記事からわかること

  • Android Studio/KotlinRecyclerViewスワイプ処理実装する方法
  • ItemTouchHelper.SimpleCallback使い方
  • スワイプ背景色アイコン設定

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

参考文献:公式リファレンス:ItemTouchHelper.SimpleCallback

環境

RecyclerViewでスワイプ処理

AndroidのRecyclerViewではリストアイテムをスワイプした際にアイテムを削除したりといったアクションを追加することが可能です。

【Kotlin/Android Studio】ItemTouchHelperの使い方!RecyclerViewでスワイプ処理を実装する方法

それを実現させるのがItemTouchHelper.SimpleCallbackクラスです。このクラスはリストアイテムのドラッグやスワイプといった仕組みを提供しています。各アクションが実行された際には適当なコールバックメソッドが呼び出されるようになっており、任意の処理を実行させることが可能になっています。


abstract class ItemTouchHelper.SimpleCallback : ItemTouchHelper.Callback

実装の流れ

実装方法は少しややこしいので流れと方法を1つずつ見ながら実装していきたいと思います。とりあえずスワイプ時に削除する処理が走るようにしていきます。

  1. ItemTouchHelperを継承したクラスの作成
  2. スワイプの方向を指定
  3. コールバックメソッド内を実装
  4. Adapter側にDeleteメソッドを用意

ItemTouchHelperを継承したクラスの作成

RecyclerViewにスワイプアクションを追加するにはItemTouchHelper.SimpleCallbackクラスを継承させます。クラス名は何でも良いですがここではSwipeToCallbackクラスを用意して引数として対象のRecyclerViewのAdapterを受け取れるようにしておきます。

class SwipeToCallback(private val adapter: MyListAdapter) : ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.ACTION_STATE_IDLE,            // ドラッグの方向を指定
    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // スワイプの方向を指定
) {
  // 〜〜〜〜〜〜〜〜〜〜〜〜〜〜
}

スワイプの方向を指定

スワイプの方向はItemTouchHelper.SimpleCallbackクラスの引数から指定します。1つ目の引数にはドラッグ方向を、2つ目の引数にはスワイプ方向を指定します。

ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.ACTION_STATE_IDLE,            // サポートしない
    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // 左と右のスワイプをサポート
)

指定するのは以下の項目です。ItemTouchHelper.UPItemTouchHelper.DOWNに関しては単体では動作確認ができず、ItemTouchHelper.LEFTなどと一緒に指定することで上や下に動かせるようになりました。また複数の値を指定したいときはorを使用します。

指定値(フラグ) 概要
ItemTouchHelper.UP 上方向
ItemTouchHelper.DOWN 下方向
ItemTouchHelper.START Viewの開始方向
ItemTouchHelper.END Viewの終了方向
ItemTouchHelper.LEFT 左方向
ItemTouchHelper.RIGHT 右方向
ItemTouchHelper.ACTION_STATE_IDLE サポートしない(厳密にはアイドル状態を示すフラグ)

その他の状態を示すフラグや詳細な内容は公式サイトを参考にしてください。

公式リファレンス:ItemTouchHelper

コールバックメソッド内を実装

スワイプ方向の指定ができたらスワイプ時に実行させたい処理を記述します。onMoveメソッドはドラッグ時の処理なのでここでは何も記述しません。onSwipedメソッド内に実行したい処理を記述します。

class SwipeToCallback(private val adapter: MyListAdapter) : ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.ACTION_STATE_IDLE,            // ドラッグの方向を指定
    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // スワイプの方向を指定
) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        // ドラッグアンドドロップの処理を実装
        return false
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        // スワイプしたアイテムを削除する処理を実装
        val position = viewHolder.adapterPosition
        adapter.deleteItem(position)
    }
}

onSwipedの引数viewHolderadapterPositionからスワイプされたリストアイテムインデックスを参照できます。少し順番が逆になりますが次でアダプター側(今回はMyListAdapter)のdeleteItemメソッドを用意します。

Adapter側にDeleteメソッドを用意

Adapterクラスに先ほど呼び出していた削除するためのメソッドを用意します。ここでは引数として受け取ったインデックス番号を元にデータソースの削除とRecyclerViewの見た目上の削除(notifyItemRemoved)を行なっています。


fun deleteItem(position: Int) {
    if (position < 0 || position >= _userList.size) {
        return
    }
    _userList.removeAt(position)
    notifyItemRemoved(position)
}

RecyclerViewとの紐付け

RecyclerViewと紐づけるためには以下のように記述します。

val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(view.context)
recyclerView.addItemDecoration(
    DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)
)
val adapter = MyListAdapter(data)
val swipeToCallback = SwipeToCallback(adapter)
val itemTouchHelper = ItemTouchHelper(swipeToCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
recyclerView.adapter = adapter

これでスワイプ時にデータを削除するような処理の実装が完了しました。この状態ではスワイプ時の背景色などを設定していないためスワイプともにそのままデータが消えていく感じなります。

スワイプ時の背景色やアイコンを設置する

スワイプ時の背景色を変更したり、アイコンを設置するにはonChildDrawメソッドをオーバーライドして記述します。


// スワイプ時の背景色とアイコンを描画
override fun onChildDraw(
    canvas: Canvas,
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    dX: Float,
    dY: Float,
    actionState: Int,
    isCurrentlyActive: Boolean
) {
    super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)

    val background: ColorDrawable = ColorDrawable(Color.RED)

    // スワイプ時のアイコンを定義AppCompatResources
    val deleteIcon: Drawable? = AppCompatResources.getDrawable(recyclerView.context,R.drawable.delete)

    if (deleteIcon != null) {

    val itemView = viewHolder.itemView
    val iconMargin = (itemView.height - deleteIcon.intrinsicHeight) / 2
    val iconTop = itemView.top + (itemView.height - deleteIcon.intrinsicHeight) / 2
    val iconBottom = iconTop + deleteIcon.intrinsicHeight

    when {
        dX > 0 -> { // 右方向へのスワイプ
            val iconLeft = itemView.left + iconMargin
            val iconRight = itemView.left + iconMargin + deleteIcon.intrinsicWidth
            deleteIcon.setBounds(iconLeft, iconTop, iconRight, iconBottom)
            background.setBounds(itemView.left, itemView.top, itemView.left + dX.toInt(), itemView.bottom)
        }
        dX < 0 -> { // 左方向へのスワイプ
            val iconLeft = itemView.right - iconMargin - deleteIcon.intrinsicWidth
            val iconRight = itemView.right - iconMargin
            deleteIcon.setBounds(iconLeft, iconTop, iconRight, iconBottom)
            background.setBounds(itemView.right + dX.toInt(), itemView.top, itemView.right, itemView.bottom)
        }
        else -> {
            background.setBounds(0, 0, 0, 0)
        }
    }

    background.draw(canvas)
    deleteIcon.draw(canvas)
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index