Android RecyclerView Swipe to delete with Icon only drawed on first element

Viewed 5833

Everything works great, but one thing is not really working. The delete icon is only rendered on the first element of the recycler view list as you can see in the image.

enter image description here enter image description here

Here is my code of the ItemTouchHelper class:

class ItemSwipeCallback(val context: Context) : ItemTouchHelper.Callback() {
    private val listeners = ArrayList<OnItemSwipe>()

    private val paint = Paint()
    val theme = context.themeId
    val icon = ContextCompat.getDrawable(context, R.drawable.ic_delete_filled_white_24dp)!!

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

        return true
    }

    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
        val direction = context.sharedPreferences.getInt(Preferences.SWIPE_DIRECTION, Preferences.SWIPE_VALUE_RIGHT)

        return when (direction) {
            Preferences.SWIPE_VALUE_RIGHT -> makeMovementFlags(0, ItemTouchHelper.RIGHT)
            Preferences.SWIPE_VALUE_LEFT -> makeMovementFlags(0, ItemTouchHelper.LEFT)
            else -> makeMovementFlags(0, ItemTouchHelper.RIGHT)
        }
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        listeners.forEach { it.onSwiped(viewHolder.layoutPosition, direction) }
    }

    override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
            dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {

        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)

        if (dX != 0f && isCurrentlyActive) {
            val itemView = viewHolder.itemView
            paint.color = Color.parseColor("#D32F2F")
            val top = (itemView.height - icon.intrinsicHeight) / 2
            val left = itemView.width - icon.intrinsicWidth - top

            if (theme == Preferences.THEME_VALUE_DARK) {
                icon.setTint(Color.BLACK)
            } else {
                icon.setTint(Color.WHITE)
            }

            if (dX < 0) {
                val background = RectF(itemView.right.toFloat() + dX, itemView.top.toFloat(),
                    itemView.right.toFloat(), itemView.bottom.toFloat())
                c.drawRect(background, paint)
                icon.setBounds(left, top, left + icon.intrinsicWidth, top + icon.intrinsicHeight)
            } else {
                val background = RectF(itemView.left.toFloat() + dX, itemView.top.toFloat(),
                    itemView.left.toFloat(), itemView.bottom.toFloat())
                c.drawRect(background, paint)
                icon.setBounds(top, top, top + icon.intrinsicWidth, top + icon.intrinsicHeight)
            }
            icon.draw(c)
        }
    }

    fun addOnItemSwipeListener(onItemSwipe: OnItemSwipe) {
        listeners.add(onItemSwipe)
    }
}

Maybe the icon which is loaded on the head of the class is only usable once? I tried already to convert it into a Bitmap and use it. I also tried loading it in the onChildDraw function.

2 Answers

The solution was too easy. I always used itemView.height instead of itemView.top.

The canvas includes all items. Not every item has its own canvas. So i have to add the height of the above items too.

The working code looks like this:

val top = itemView.top + (itemView.height - intrinsicHeight) / 2
val left = itemView.width - intrinsicWidth - (itemView.height - intrinsicHeight) / 2
val right = left + intrinsicHeight
val bottom = top + intrinsicHeight

if (dX < 0) {
    background.setBounds(itemView.right + dX.toInt(), itemView.top, itemView.right, itemView.bottom)
    icon.setBounds(left, top, right, bottom)
} else if (dX > 0) {
    background.setBounds(itemView.left + dX.toInt(), itemView.top, itemView.left, itemView.bottom)
    icon.setBounds(top, top, top, bottom)
}
background.draw(c)
icon.draw(c)

Did u check the value of this parameter: isCurrentlyActive?

I guess there is no error in image creation(icon). because the image is successfully created first time. So, the problem is in looping.

  if (dX < 0) {...}else{...}

Here, No matter the value of dX, the image is gonna added to children(row).

if (dX != 0f && isCurrentlyActive) 

This is the only check point(considerably) in your code. Technically the whole block will skipped, if isCurrentlyActive boolean is false.

Related