I am using android recyclerView, now on the swipe of item in recycler view I want to show two views, one for delete and one for mute, Now on click of view, the views(delete/mute) should go back and content should be in previous form, at a time only one row must be swiped
The swipeHelperClass I used is as
abstract class SwipeHelper(
var context: Context,
private val recyclerView: RecyclerView
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.ACTION_STATE_IDLE,
ItemTouchHelper.LEFT
) {
var ct = context
private var swipedPosition = -1
private var previousSwipedPosition = -1
private val buttonsBuffer: MutableMap<Int, List<UnderlayButton>> = mutableMapOf()
private val recoverQueue = object : LinkedList<Int>() {
override fun add(element: Int): Boolean {
if (contains(element)) return false
return super.add(element)
}
}
override fun getSwipeThreshold(viewHolder: ViewHolder): Float {
return mSwipeThreshold
}
override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
return 0.1f * defaultValue
}
override fun getSwipeVelocityThreshold(defaultValue: Float): Float {
return 5.0f * defaultValue
}
@SuppressLint("ClickableViewAccessibility")
private val touchListener = View.OnTouchListener { _, event ->
if (swipedPosition < 0) return@OnTouchListener false
buttonsBuffer[swipedPosition]?.forEach { it.handle(event) }
recoverQueue.add(swipedPosition)
Log.d("swipePos", "$swipedPosition")
swipedPosition = -1
recoverSwipedItem()
true
}
init {
recyclerView.setOnTouchListener(touchListener)
}
private fun recoverSwipedItem() {
while (!recoverQueue.isEmpty()) {
val position = recoverQueue.poll() ?: return
recyclerView.adapter?.notifyItemChanged(position)
}
}
private fun drawButtons(
canvas: Canvas,
buttons: List<UnderlayButton>,
itemView: View,
dX: Float
) {
var right = itemView.right
buttons.forEach { button ->
val width = button.intrinsicWidth / buttons.intrinsicWidth() * abs(dX)
val left = right - width
button.draw(
canvas,
RectF(left, itemView.top.toFloat(), right.toFloat(), itemView.bottom.toFloat())
)
right = left.toInt()
}
}
fun hideViews() {
if (swipedPosition != -1) {
val viewHolder = recyclerView.findViewHolderForAdapterPosition(swipedPosition)
viewHolder?.itemView?.animate()?.translationX(0f)?.start()
swipedPosition = -1
clearView(recyclerView, viewHolder!!)
}
}
private lateinit var mCanvas: Canvas
private lateinit var swipedViewHolder: ViewHolder
private lateinit var buttons: List<UnderlayButton>
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
mCanvas = c
val position = viewHolder.absoluteAdapterPosition
var maxDX = dX
val itemView = viewHolder.itemView
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if (dX < 0) {
buttons = instantiateUnderlayButton(position)
buttonsBuffer[position] = buttons
val buttons = buttonsBuffer[position] ?: return
if (buttons.isEmpty()) return
maxDX = max(-buttons.intrinsicWidth(), dX)
drawButtons(c, buttons, itemView, maxDX)
}
}
super.onChildDraw(
c,
recyclerView,
viewHolder,
maxDX,
dY,
actionState,
isCurrentlyActive
)
}
override fun onSelectedChanged(viewHolder: ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
Log.d(TAG, "onSelectedChanged: ")
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE && viewHolder != null) {
viewHolder.itemView.setBackgroundColor(
ContextCompat.getColor(
context,
R.color.bckgRecyclerViewColor
)
)
}
}
override fun clearView(
@NonNull recyclerView: RecyclerView,
@NonNull viewHolder: ViewHolder
) { //called when you dropped the item
Log.d(TAG, "clearView: ")
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.white))
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder,
target: ViewHolder
): Boolean {
return false
}
private val TAG = "SwipeHelper"
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
Log.d(TAG, "onSwiped => position : $position prevPosition : $previousSwipedPosition")
previousSwipedPosition = position
if (swipedPosition != position)
recoverQueue.add(swipedPosition)
swipedPosition = position
recoverSwipedItem()
swipedViewHolder = viewHolder
mSwipeThreshold = 0.5F * buttons.size * 60
}
private var mSwipeThreshold: Float = 0F
abstract fun instantiateUnderlayButton(position: Int): List<UnderlayButton>
//region UnderlayButton
interface UnderlayButtonClickListener {
fun onClick()
}
class UnderlayButton(
private val context: Context,
@ColorRes private val colorRes: Int,
private val icon: Int,
private val clickListener: UnderlayButtonClickListener
) {
private var clickableRegion: RectF? = null
private val horizontalPadding = 84.0f
val intrinsicWidth: Float
init {
val paint = Paint()
paint.typeface = Typeface.DEFAULT_BOLD
paint.textAlign = Paint.Align.LEFT
val titleBounds = Rect()
intrinsicWidth = titleBounds.width() + 2 * horizontalPadding
}
fun draw(canvas: Canvas, rect: RectF) {
val paint = Paint()
paint.color = ContextCompat.getColor(context, colorRes)
canvas.drawRect(rect, paint)
paint.color = ContextCompat.getColor(context, android.R.color.white)
paint.typeface = Typeface.DEFAULT_BOLD
paint.textAlign = Paint.Align.LEFT
val background_dest =
RectF(rect.left + 30, rect.top + 30, rect.right - 30, rect.bottom - 30)
canvas.drawBitmap(getBitmapMarker(context, icon), null, background_dest, paint)
clickableRegion = background_dest
}
fun handle(event: MotionEvent) {
clickableRegion?.let {
if (it.contains(event.x, event.y)) {
clickListener.onClick()
}
}
}
}
}
fun getBitmapMarker(ct: Context, icon: Int): Bitmap {
val customMarkerView: View =
(ct.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?)!!.inflate(
icon,
null
)
customMarkerView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
customMarkerView.layout(
0,
0,
customMarkerView.measuredWidth,
customMarkerView.measuredHeight
)
customMarkerView.buildDrawingCache()
val returnedBitmap = Bitmap.createBitmap(
customMarkerView.measuredWidth, customMarkerView.measuredHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(returnedBitmap)
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN)
val drawable = customMarkerView.background
drawable?.draw(canvas)
customMarkerView.draw(canvas)
return returnedBitmap
}
private fun List<SwipeHelper.UnderlayButton>.intrinsicWidth(): Float {
if (isEmpty()) return 0.0f
return map { it.intrinsicWidth }.reduce { acc, fl -> acc + fl }
}
```. **its initialization is as**
`
ItemTouchHelper(object : SwipeHelper(requireContext(), vb.rvPriority) { override fun instantiateUnderlayButton(position: Int): List { val deleteButton = deleteButton(position) val muteUnmuteButton = muteUnmute(position, this) return listOf(deleteButton, muteUnmuteButton) } }).attachToRecyclerView(vb.rvPriority)
**function muteUnmute is as **
private fun muteUnmute(
position: Int, swipeHelper: SwipeHelper
): SwipeHelper.UnderlayButton {
val id =
if (recentUserList[position].chat_muted) R.layout.unmute_button else R.layout.mute_button
return SwipeHelper.UnderlayButton(
requireContext(),
R.color.black,
id,
object : SwipeHelper.UnderlayButtonClickListener {
override fun onClick() {
userAdapter.getItemAt(position)?.let { item ->
if (item.blue_ring || item.red_ring) {
"You can't mute this chat because you have active order present with this user.".showToast(
requireContext()
)
} else {
// mute chat
if (userAdapter.getItemAt(position)?.chat_muted == false) {
userAdapter.getItemAt(position)?.chat_muted = true
chatHelper.muteChat(userAdapter.getItemAt(position)?.id ?: "")
userAdapter.notifyItemChanged(position)
} else { // unmute chat
userAdapter.getItemAt(position)?.chat_muted = false
chatHelper.unmuteChat(userAdapter.getItemAt(position)?.id ?: "")
userAdapter.notifyItemChanged(position)
}
swipeHelper.hideViews()
vb.rvPriority.smoothScrollToPosition(position)
}
}
}
})
}
**onClick on muteUnmute recyclerView go back to its original view, but I am unable to achieve one item swiped at a time** [](https://i.stack.imgur.com/b3tVi.png)