效果:
代码示例:
通过自定义ItemTouchHelper,在Adapter中进行使用:
private val itemTouchCallback by lazy { LeftDeleteTouchCallback() }
private val itemTouchHelper by lazy { ItemTouchHelper(itemTouchCallback) }
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
TLog.d(TAG, "onAttachedToRecyclerView: $recyclerView")
recyclerView.addRecyclerListener {
itemTouchCallback.resetViewHolder(it)
}
itemTouchHelper.attachToRecyclerView(recyclerView)
}
class LeftDeleteTouchCallback : ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
return makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
}
override fun isLongPressDragEnabled(): Boolean {
return false
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder,
): Boolean {
return false
}
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
return Int.MAX_VALUE.toFloat()
}
override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
return Int.MAX_VALUE.toFloat()
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
/**
* 上次交互的viewholder
*/
private var preActionViewHolder: RecyclerView.ViewHolder? = null
/**
* 首次进入交互状态
*/
private var firstInteract: Boolean = false
/**
* 当前ViewHolderScrollX
*/
private var mCurrentScrollX: Int = 0
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean,
) {
val tvDelete = viewHolder.itemView.findViewById<View>(R.id.btn_delete)
if (firstInteract) {
//记录首次进入的scrollX, 用于后续根据偏移量设置itemView位置
mCurrentScrollX = viewHolder.itemView.scrollX
firstInteract = false
}
//偏移量允许介于tvDelete宽度和0之间
val mDx = (mCurrentScrollX - dX.toInt()).coerceAtMost(tvDelete.width).coerceAtLeast(0)
if (isCurrentlyActive) {
viewHolder.itemView.scrollTo(mDx, 0)
} else {
//已经抬起手指
if (firstInteract) return
firstInteract = true
//记录上次交互的preActionViewHolder
viewHolder.itemView.scrollTo(if (mDx >= tvDelete.width / 2) tvDelete.width else 0, 0)
preActionViewHolder = if (mDx >= (tvDelete.width / 2)) viewHolder else null
}
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
viewHolder ?: return
if (actionState != ItemTouchHelper.ACTION_STATE_SWIPE) return
if (preActionViewHolder != viewHolder) {
resetPreViewHolder()
}
}
/**
* smooth还原到开始状态
*/
fun resetPreViewHolder() {
val x = preActionViewHolder?.itemView?.scrollX ?: 0
if (x == 0) return
val animator = ValueAnimator.ofInt(x, 0)
val animationView = preActionViewHolder
animator.addUpdateListener {
animationView?.itemView?.scrollTo(it.animatedValue as Int, 0)
}
animator.start()
}
fun resetViewHolder(viewHolder: RecyclerView.ViewHolder) {
val x = preActionViewHolder?.itemView?.scrollX ?: 0
if (x == 0) return
if (preActionViewHolder != viewHolder) return
viewHolder.itemView.scrollTo(0, 0)
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="@dimen/ccui_dp_60"
android:layout_marginBottom="@dimen/ccui_dp_16"
android:background="@drawable/bg_highlight">
<TextView
android:id="@+id/btn_delete"
android:layout_width="@dimen/ccui_dp_90"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@drawable/bg_delete"
android:gravity="center"
android:text="@string/delete"
android:textColor="@color/normal_color"
android:textSize="@dimen/font_normal_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
android:id="@+id/layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_alarm_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/ccui_dp_20"
android:layout_marginTop="@dimen/ccui_dp_10"
android:layout_marginEnd="@dimen/ccui_dp_18"
android:layout_toStartOf="@id/iw_alarm_enabler"
android:includeFontPadding="false"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/normal_color"
android:textSize="@dimen/font_normal_size"
tools:text="多喝热水" />
<TextView
android:id="@+id/tv_alarm_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_alarm_name"
android:layout_marginStart="@dimen/ccui_dp_20"
android:layout_marginEnd="@dimen/ccui_dp_18"
android:layout_toStartOf="@id/iw_alarm_enabler"
android:includeFontPadding="false"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/dim50_color"
android:textSize="@dimen/ccui_sp_14"
tools:text="多喝热水" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/iw_alarm_enabler"
android:layout_width="@dimen/ccui_dp_56"
android:layout_height="@dimen/ccui_dp_32"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/ccui_dp_20"
android:background="@drawable/selector_switch_bg"
android:checked="true"
android:thumb="@null"
android:track="@null"
app:splitTrack="true" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
## github地址
https://github.com/dongpingwang/RvSlideDeleteDemo