【Android】画面コンポーネント / RecyclerView ~ スワイプして項目削除 ~

■ はじめに

https://dk521123.hatenablog.com/entry/2020/07/21/000000

の続き。

今回は、スワイプして項目を削除する機能を追加する

■ ポイント

ItemTouchHelper() を使って実装する

■ サンプル

MainActivity.kt

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dk.englishcards.R
import com.dk.englishcards.cards.EnglishCard
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        addFloatingActionButton.setOnClickListener {
            super.moveToEdit()
        }
    }

    override fun onResume() {
        super.onResume()

        val englishCards = super.dbHandler.readAll()
        val adapter = MainListRecyclerViewAdapter(englishCards.toMutableList())
        mainRecyclerView.layoutManager = LinearLayoutManager(this)
        mainRecyclerView.adapter = adapter
        mainRecyclerView.setHasFixedSize(true)
        adapter.setOnItemClickListener(
            object: MainListRecyclerViewAdapter.OnItemClickListener {
                override fun onItemClickListener(
                    view: View,
                    position: Int,
                    targerItem: EnglishCard) {
                    moveTo(
                        EditActivity::class.java,
                        EnglishCard.ID_FIELD,
                        targerItem.englishCardId)
                }
            })

        // ★ここ★
        val helper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
            0, (ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT)
        ) {
            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                return false
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                adapter.removeItem(viewHolder.adapterPosition)
            }

            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 isFromLeftDirection = dx < 0
                val itemView = viewHolder.itemView

                val background = ColorDrawable(Color.RED)
                if (isFromLeftDirection) {
                    background.setBounds(
                        itemView.right + dx.toInt(),
                        itemView.top,
                        itemView.right,
                        itemView.bottom
                    )
                } else {
                    background.setBounds(
                        itemView.left,
                        itemView.top,
                        itemView.left + dx.toInt(),
                        itemView.bottom
                    )
                }
                background.draw(canvas)

                val deleteIcon = AppCompatResources.getDrawable(
                    this@MainActivity,
                    R.drawable.ic_baseline_delete_forever_24
                )
                val iconMarginVertical =
                    (viewHolder.itemView.height - deleteIcon!!.intrinsicHeight) / 2
                val itemHeight = itemView.bottom - itemView.top
                val deleteIconIntrinsicWidth = deleteIcon?.intrinsicWidth
                val deleteIconIntrinsicHeight = deleteIcon?.intrinsicHeight
                if (isFromLeftDirection) {
                    val deleteIconTop =
                        itemView.top + (itemHeight - deleteIconIntrinsicHeight) / 2
                    val deleteIconMargin =
                        (itemHeight - deleteIconIntrinsicHeight) / 2
                    deleteIcon.setBounds(
                        itemView.right - deleteIconMargin - deleteIconIntrinsicWidth,
                        deleteIconTop,
                        itemView.right - deleteIconMargin,
                        deleteIconTop + deleteIconIntrinsicHeight)
                } else {
                    deleteIcon.setBounds(
                        itemView.left + iconMarginVertical,
                        itemView.top + iconMarginVertical,
                        itemView.left + iconMarginVertical + deleteIcon.intrinsicWidth,
                        itemView.bottom - iconMarginVertical
                    )
                }
                deleteIcon.draw(canvas)
            }
        })
        helper.attachToRecyclerView(mainRecyclerView)
    }
}

MainListRecyclerViewAdapter.kt

class MainListRecyclerViewAdapter(
    private val targetList: MutableList<EnglishCard>) :
    RecyclerView.Adapter<MainListRecyclerViewAdapter.MainListViewHolder>() {
    private val dbHandler = EnglishCardDbHandler()
    private lateinit var listener: OnItemClickListener

    // 略

    // 削除用メソッド
    fun removeItem(removingIndex: Int) {
        if (targetList.size <= 0) {
            return
        }

        // DB削除
        val targetEnglishCard = targetList[removingIndex]
        dbHandler.delete(targetEnglishCard.englishCardId)

        // リスト削除
        this.targetList.removeAt(removingIndex)
        // notifyItemRemovedを呼ぶ
        this.notifyItemRemoved(removingIndex)
    }
}

参考文献

https://qiita.com/kkt_yu/items/baacbee06a8868c08c94
https://qiita.com/karass/items/b0e09c81d413224d0010
https://qiita.com/Hachimori/items/82eac873286ec2aa22fb
https://qiita.com/shts/items/042f57c3451b9375db1d
https://nonylene.hatenablog.jp/entry/2016/09/06/000331
https://qiita.com/shts/items/042f57c3451b9375db1d

関連記事

レイアウト ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2015/08/23/165632
画面コンポーネント / RecyclerView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/07/21/000000
Kotlin / Realm で英単語帳を作る
https://dk521123.hatenablog.com/entry/2020/07/20/232009