RecyclerViewでスワイプの削除

# はじめに
ここでは、RecyclerViewのアイテムをスワイプ削除する方法について書いていきます。 これまでの実装については[前回](https://qiita.com/karass/items/a62d27a16b185abf47f0)を参照してください。

# 目標
今回は下のGIFのようにRecyclerViewのアイテムをスワイプで削除できようにします。
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289190/57c95ee3-56b0-c60f-3d7f-d1718356313d.gif">

# 実装
Gradleには、特に追加するものはありません。
## Activity
削除機能を実装するためのコードを、今回はActivityに書きました。

```MainActivity.kt
class MainActivity : AppCompatActivity() {

    ...
    private val swipeToDismissTouchHelper by lazy {
        getSwipeToDismissTouchHelper(adapter)
    }

    override fun onCreate(savedInstanceState: Bundle?) {

        ...

        swipeToDismissTouchHelper.attachToRecyclerView(main_recycler_view)

        ...

        }

    private fun getSwipeToDismissTouchHelper(adapter: RecyclerAdapter) =
        ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
            ItemTouchHelper.ACTION_STATE_IDLE,
            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 timeTasks = adapter.getItems()
                mainViewModel.delete(timeTasks[viewHolder.adapterPosition])
            }

            override fun onChildDraw(
                c: Canvas,
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                dX: Float,
                dY: Float,
                actionState: Int,
                isCurrentlyActive: Boolean
            ) {
                super.onChildDraw(
                    c,
                    recyclerView,
                    viewHolder,
                    dX,
                    dY,
                    actionState,
                    isCurrentlyActive
                )
                val itemView = viewHolder.itemView
                val background = ColorDrawable(Color.RED)
                val deleteIcon = AppCompatResources.getDrawable(
                    this@MainActivity,
                    R.drawable.ic_baseline_delete_36
                )
                val iconMarginVertical =
                    (viewHolder.itemView.height - deleteIcon!!.intrinsicHeight) / 2

                deleteIcon.setBounds(
                    itemView.left + iconMarginVertical,
                    itemView.top + iconMarginVertical,
                    itemView.left + iconMarginVertical + deleteIcon.intrinsicWidth,
                    itemView.bottom - iconMarginVertical
                )
                background.setBounds(
                    itemView.left,
                    itemView.top,
                    itemView.right + dX.toInt(),
                    itemView.bottom
                )
                background.draw(c)
                deleteIcon.draw(c)
            }
        })
}

```
なんかともて長いコードになってしまった。
細分化して、何をしているか見ていきます。

```kotlin

ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
            ItemTouchHelper.ACTION_STATE_IDLE,
            ItemTouchHelper.RIGHT
        )
```
`ItemTouchHelper()`はRecyclerViewでドラッグ＆ドロップやスワイプ機能を実現するためのクラスです。
ユーザの操作に対してCallbackを受け取る必要があるので、それを簡単に実装できる`ItemTouchHelper.SimpleCallback()`を使用しました。
`ItemTouchHelper.SimpleCallback()`は第一引数にドラッグを有効にする向きを指定し、第二引数でスワイプを有効にする向きを指定します。今回は、ドラッグは行わないので`ItemTouchHelper.ACTION_STATE_IDLE`を指定し、スワイプは右スワイプ（`ItemTouchHelper.RIGHT`）のみを有効にします。


```kotlin
override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                return false
            }
```
`onMove()`では、ドラッグの処理を書くのですが今回はドラッグの処理はないので何も変更なしです。

```kotlin
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                val timeTasks = adapter.getItems()
                mainViewModel.delete(timeTasks[viewHolder.adapterPosition])
            }
```
`onSwiped（）`では、スワイプ時の処理を書きます。
スワイプ時にadapterからアイテムを要求し、スワイプされたアイテムをviewModelに渡しています。

```kotlin
override fun onChildDraw(
                c: Canvas,
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                dX: Float,
                dY: Float,
                actionState: Int,
                isCurrentlyActive: Boolean
            )
```
`onChildDraw()`では、スワイプ時の背景やアイコンの設定ができます。（他にも方法はあるらしい）

変数を定義

```kotlin
val itemView = viewHolder.itemView // RecyclerViewのitemView
val background = ColorDrawable(Color.RED) // スワイプ時の背景の定義
val deleteIcon = AppCompatResources.getDrawable( // スワイプ時に出てくるアイコンを定義
    this@MainActivity,
    R.drawable.ic_baseline_delete_36
)
// アイコンを表示させる位置を決めるためのマージンを計算（今回は縦方向の中心）
val iconMarginVertical =
    (viewHolder.itemView.height - deleteIcon!!.intrinsicHeight) / 2
```

最後に、背景とアイコンを適切な位置で表示させます。

```kotlin
deleteIcon.setBounds(
    itemView.left + iconMarginVertical,
    itemView.top + iconMarginVertical,
    itemView.left + iconMarginVertical + deleteIcon.intrinsicWidth,
    itemView.bottom - iconMarginVertical
)
background.setBounds(
    itemView.left,
    itemView.top,
    itemView.right + dX.toInt(),
    itemView.bottom
)
background.draw(c)
deleteIcon.draw(c)
```