You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android RecyclerView左滑25%显示删除文本并点击触发删除实现

需求调整:RecyclerView左滑显示删除按钮,点击才触发删除

现有DragAndDropAdapterForShoppingList实现了RecyclerView左滑超过50%触发删除回调,现在需要修改为:左滑25%时显示“删除”文本,保持滑动状态,点击该文本才执行删除操作。

现有代码

class DragAndDropAdapterForShoppingList(
    val adapter: ShoppingListAdapter,
    swipeDirs: Int,
    private val onSwapToDelete: SingleLiveData<ShoppingListContentWidget.CallToAction>
) :
    ItemTouchHelper.SimpleCallback(0, swipeDirs) {

    private var listAdapter = adapter

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

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        Timber.log(1, "")
        onSwapToDelete.value = ShoppingListContentWidget.CallToAction.SwappedToDelete(
            (
                adapter.items[
                    viewHolder
                        .adapterPosition
                ] as ShoppingListUIItem
                )
        )
        listAdapter.deleteItem(viewHolder.adapterPosition)
    }

    override fun onChildDraw(
        c: Canvas,
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        dX: Float,
        dY: Float,
        actionState: Int,
        isCurrentlyActive: Boolean,
    ) {
        val foregroundView = getForegroundView(viewHolder)
        getDefaultUIUtil().onDraw(
            c,
            recyclerView,
            foregroundView,
            dX,
            dY,
            actionState,
            isCurrentlyActive
        )
    }

    override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
        val foregroundView = getForegroundView(viewHolder)
        getDefaultUIUtil().clearView(foregroundView)
    }

    override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        if (viewHolder != null) {
            val foregroundView = getForegroundView(viewHolder)
            getDefaultUIUtil().onSelected(foregroundView)
        }
    }

    override fun onChildDrawOver(
        c: Canvas,
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder?,
        dX: Float,
        dY: Float,
        actionState: Int,
        isCurrentlyActive: Boolean,
    ) {
        val foregroundView = getForegroundView(viewHolder)
        getDefaultUIUtil().onDrawOver(
            c,
            recyclerView,
            foregroundView,
            dX,
            dY,
            actionState,
            isCurrentlyActive
        )
    }

    override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
        return if ((adapter.items[viewHolder.adapterPosition] is ShoppingListUIItem.SpecificItem)) {
            super.getSwipeDirs(recyclerView, viewHolder)
        } else {
            if ((adapter.items[viewHolder.adapterPosition] as ShoppingListUIItem.GenericItem).name?.isEmpty() == true ||
                (adapter.items[viewHolder.adapterPosition] as ShoppingListUIItem.GenericItem).name == null
            ) {
                0
            } else {
                super.getSwipeDirs(recyclerView, viewHolder)
            }
        }
    }

    private fun getForegroundView(viewHolder: RecyclerView.ViewHolder?): ConstraintLayout? {
        return (viewHolder as? ShoppingListGenericItemViewHolder)?.genericItemViewFrontView
            ?: (viewHolder as? ShoppingListSpecificItemViewHolder)?.specificItemFrontView
    }
}

实现方案

1. 为列表项布局添加删除后台视图

在RecyclerView的item布局中添加一个用于显示“删除”的TextView,作为后台视图,默认被前景视图覆盖。示例布局代码:

<TextView
    android:id="@+id/tv_delete"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:background="@color/red"
    android:text="删除"
    android:textColor="@color/white"
    android:gravity="center"
    android:paddingHorizontal="24dp"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"/>

确保前景视图(即原来的genericItemViewFrontView/specificItemFrontView)层级在该TextView之上。

2. 在Adapter中新增获取删除视图的方法

在DragAndDropAdapterForShoppingList中添加方法,用于从ViewHolder中获取删除按钮:

private fun getDeleteView(viewHolder: RecyclerView.ViewHolder?): TextView? {
    return (viewHolder as? ShoppingListGenericItemViewHolder)?.tvDelete
        ?: (viewHolder as? ShoppingListSpecificItemViewHolder)?.tvDelete
}

同时需要在对应的ViewHolder类中初始化tvDelete控件,绑定布局中的@+id/tv_delete。

3. 修改滑动阈值为25%

重写getSwipeThreshold方法,将滑动触发阈值设置为25%:

override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
    return 0.25f // 左滑超过item宽度的25%时进入可保持状态
}

4. 禁用自动删除逻辑

修改onSwiped方法,清空原有自动删除逻辑,因为删除操作将由点击按钮触发:

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
    // 空实现,删除操作由点击按钮触发
}

5. 控制滑动距离并添加删除按钮点击事件

修改onChildDraw方法,限制最大滑动距离为删除按钮的宽度,同时给删除按钮绑定点击事件:

override fun onChildDraw(
    c: Canvas,
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    dX: Float,
    dY: Float,
    actionState: Int,
    isCurrentlyActive: Boolean,
) {
    val foregroundView = getForegroundView(viewHolder) ?: return
    val deleteView = getDeleteView(viewHolder) ?: return

    // 限制最大滑动距离为删除按钮的宽度,避免过度滑动
    val maxSwipeDistance = deleteView.width.toFloat()
    val adjustedDX = if (dX < -maxSwipeDistance) -maxSwipeDistance else dX

    // 绘制前景视图的滑动效果
    getDefaultUIUtil().onDraw(
        c,
        recyclerView,
        foregroundView,
        adjustedDX,
        dY,
        actionState,
        isCurrentlyActive
    )

    // 给删除按钮设置点击事件,仅初始化一次(避免ViewHolder复用重复绑定)
    if (deleteView.tag != true) {
        deleteView.tag = true
        deleteView.setOnClickListener {
            val position = viewHolder.adapterPosition
            if (position != RecyclerView.NO_POSITION) {
                // 执行原有删除逻辑
                onSwapToDelete.value = ShoppingListContentWidget.CallToAction.SwappedToDelete(
                    adapter.items[position] as ShoppingListUIItem
                )
                listAdapter.deleteItem(position)
                // 重置视图状态
                recyclerView.findViewHolderForAdapterPosition(position)?.let { holder ->
                    getDefaultUIUtil().clearView(getForegroundView(holder))
                }
            }
        }
    }
}

6. 优化视图复位逻辑

修改clearView方法,清除删除按钮的点击标记,避免ViewHolder复用导致的事件混乱:

override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
    val foregroundView = getForegroundView(viewHolder)
    getDefaultUIUtil().clearView(foregroundView)
    // 清除标记,确保复用ViewHolder时能重新绑定点击事件
    getDeleteView(viewHolder)?.tag = false
}

7. 可选:调整滑动灵敏度

如果需要避免轻微滑动就复位,可以重写getSwipeEscapeVelocity提高滑动逃逸速度:

override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
    return defaultValue * 2 // 提高阈值,需要更快的滑动才能保持展开状态
}

内容的提问来源于stack exchange,提问作者MARSH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:17:03