Skip to content

Instantly share code, notes, and snippets.

@molidev8
Last active June 19, 2022 14:10
Show Gist options
  • Save molidev8/4eb73ff92584cd15f9376ef03e24a321 to your computer and use it in GitHub Desktop.
Save molidev8/4eb73ff92584cd15f9376ef03e24a321 to your computer and use it in GitHub Desktop.
A swipe animation over an item in RecyclerView
private val trashIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_delete_24)
private val circleColor = ContextCompat.getColor(context, R.color.deleteRed)
private val circlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = circleColor }
private val reverseSurfaceColor = ContextCompat.getColor(context, R.color.primaryTextColor)
private val CIRCLE_ACCELERATION = 6f
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
// We don't want to do anything unless the view is being swiped
if (dX == 0f) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
return
}
val left = viewHolder.itemView.left.toFloat()
val top = viewHolder.itemView.top.toFloat()
val right = viewHolder.itemView.right.toFloat()
val bottom = viewHolder.itemView.bottom.toFloat()
// Android draws with the center of the axis in the top left corner
val width = right - left
val height = bottom - top
// Saves the canvas state to restore it at the end
val saveCount = c.save()
var iconColor = circleColor
// Limits the child view to the space left by the viewholder while its being swiped
c.clipRect(left, top, left + dX, bottom)
c.drawColor(ContextCompat.getColor(context, R.color.colorSurfaceAccent))
// The percentage that the child view has moved in the X axis
val progress = dX / width
val swipeThreshold = getSwipeThreshold(viewHolder)
val iconPopThreshold = swipeThreshold + 0.125f
val iconPopFinishedThreshold = iconPopThreshold + 0.125f
var circleRadius = 0f
val iconScale: Float
when (progress) {
in 0f..swipeThreshold -> {
iconScale = 1f - (progress * 0.2f)
}
else -> {
// The radius is the progress relative to the swipeThreshold multiplied by the width and the acceleration
// The usage of the width allows the radius to adapt to the different screen sizes dynamically in every device
circleRadius = (progress - swipeThreshold) * width * CIRCLE_ACCELERATION
iconColor = reverseSurfaceColor
iconScale = when(progress) {
in iconPopThreshold..iconPopFinishedThreshold -> 1.2f - progress * 0.2f
else -> 1f
}
}
}
trashIcon?.let {
// 64 is the padding of the icon, divided by 2 to get the center of the icon
val centerInXAxis = left + 64 + it.intrinsicWidth / 2f
val centerInYAxis = top + 64 + it.intrinsicHeight / 2f
// Sets the position of the icon inside the child view
it.setBounds(
(centerInXAxis - it.intrinsicWidth * iconScale).toInt(),
(centerInYAxis - it.intrinsicHeight * iconScale).toInt(),
(centerInXAxis + it.intrinsicWidth * iconScale).toInt(),
(centerInYAxis + it.intrinsicHeight * iconScale).toInt()
)
// Sets the color of the icon
it.colorFilter = PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
if (circleRadius > 0) {
c.drawCircle(centerInXAxis, centerInYAxis, circleRadius, circlePaint)
}
it.draw(c)
}
c.restoreToCount(saveCount)
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment