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
相关产品推荐
相关产品推荐

