RecyclerView 条目滑动删除辅助类

106 阅读1分钟

RecyclerView 条目滑动删除辅助类

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.MotionEvent
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.benefit.workbox.widget.R

class SwipeDeleteHelper(
    private val context: Context,
    private val recyclerView: RecyclerView,
    private val itemWidthDp: Int = 88, // 删除区域宽度(dp)
    private val revealThreshold: Float = 0.3f, // 显示删除图标的阈值比例
    private val deleteIconResId: Int = R.drawable.svg_gray_delete, // 删除图标资源ID
    private val backgroundColor: Int = Color.TRANSPARENT, // 背景颜色
    private val onItemDeleteListener: (position: Int) -> Unit, // 删除回调
) {

    private val itemWidthPx: Int
    private var swipingViewHolder: RecyclerView.ViewHolder? = null
    private var deleteAreaBounds: android.graphics.Rect? = null

    init {
        itemWidthPx = dpToPx(context, itemWidthDp)
        setupSwipeToDelete()
        setupTouchListener()
    }

    private fun setupSwipeToDelete() {
        val simpleCallback = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

            private val background = ColorDrawable(backgroundColor)
            private val deleteIcon = ContextCompat.getDrawable(context, deleteIconResId)
            private val intrinsicWidth = deleteIcon?.intrinsicWidth ?: 0
            private val intrinsicHeight = deleteIcon?.intrinsicHeight ?: 0

            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder,
            ): Boolean {
                return false
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                // 不自动删除,由区域点击触发
            }

            override fun onChildDraw(
                c: Canvas,
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                dX: Float,
                dY: Float,
                actionState: Int,
                isCurrentlyActive: Boolean,
            ) {
                // 限制最大滑动距离
                val limitedDx = -itemWidthPx.coerceAtMost(-dX.toInt()).toFloat()

                // 计算滑动比例
                val swipeProgress = Math.abs(limitedDx) / itemWidthPx

                val itemView = viewHolder.itemView
                val itemHeight = itemView.bottom - itemView.top

                // 绘制删除背景(覆盖整个删除区域)
                background.alpha = (255 * swipeProgress).toInt()
                background.setBounds(
                    itemView.right - itemWidthPx,
                    itemView.top,
                    itemView.right,
                    itemView.bottom
                )
                background.draw(c)

                // 保存删除区域的边界,用于点击检测
                if (isCurrentlyActive) {
                    deleteAreaBounds = android.graphics.Rect(
                        itemView.right - itemWidthPx,
                        itemView.top,
                        itemView.right,
                        itemView.bottom
                    )
                    swipingViewHolder = viewHolder
                }

                // 只有当滑动超过阈值时才显示删除图标
                if (swipeProgress >= revealThreshold) {
                    // 计算删除图标位置(在删除区域内居中)
                    val iconLeft = itemView.right - itemWidthPx + (itemWidthPx - intrinsicWidth) / 2
                    val iconRight = iconLeft + intrinsicWidth
                    val iconTop = itemView.top + (itemHeight - intrinsicHeight) / 2
                    val iconBottom = iconTop + intrinsicHeight

                    deleteIcon?.setBounds(iconLeft, iconTop, iconRight, iconBottom)
                    deleteIcon?.draw(c)
                }

                // 移动内容视图
                itemView.translationX = limitedDx
            }

            override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
                return 0.5f // 需要滑动超过一半才认为完成滑动
            }
        }

        val itemTouchHelper = ItemTouchHelper(simpleCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun setupTouchListener() {
        recyclerView.setOnTouchListener { _, event ->
            handleTouchEvent(event)
            false
        }
    }

    private fun handleTouchEvent(event: MotionEvent) {
        if (event.action == MotionEvent.ACTION_UP && swipingViewHolder != null && deleteAreaBounds != null) {
            val viewHolder = swipingViewHolder!!
            val bounds = deleteAreaBounds!!

            // 检查触摸点是否在删除区域内
            if (bounds.contains(event.x.toInt(), event.y.toInt())) {
                val position = viewHolder.bindingAdapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    // 触发删除回调
                    onItemDeleteListener(position)
                    // 重置状态
                    swipingViewHolder = null
                    deleteAreaBounds = null
                }
            }

        }
    }

    private fun dpToPx(context: Context, dp: Int): Int {
        val density = context.resources.displayMetrics.density
        return (dp * density).toInt()
    }
}

使用方法

SwipeDeleteHelper(
    context = this,
    recyclerView = binding.rvList,
) { position ->
}

展示效果

wechat_del.png