Itemtouchhelper : Limit Swipe Width Of Itemtouchhelper.simplecallback On Recyclerview
I have successfully implemented swipe behavior and performed some actions with it. The problem now I have is I want to limit the swipe width when I swipe the item. Currently this i
Solution 1:
Your onChildDraw()
method at the end calls its super super method like so:
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
To limit the maximum width of your swipe you can invoke super.onChildDraw()
with a reduced dX
:
super.onChildDraw(c, recyclerView, viewHolder, dX / 4, dY, actionState, isCurrentlyActive);
Solution 2:
Following the response from Alexander Thiel I created a class that manages Left and Right Item Swipe:
/**
* Adds [RecyclerView] items decoration shown when the user swipes an item horizontally (left or right).
* When the item is being swiped to left/right, customizable views are shown (check MEMBERS section).
*
* USAGE:
* val callback = ItemTouchSwipeHelperCallback(recyclerView)
* callback.onItemSwipeListener = this
* callback.rightBackgroundColor = R.color.red
* // ... Any other type of configuration
*
*/classItemTouchSwipeHelperCallback(
privateval recyclerView: RecyclerView
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.ACTION_STATE_IDLE,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
privateval context: Context
get() = recyclerView.context
/* ------------------- VIEW DEFAULTS ------------------- */privateval defaultIconSizeDp = 28privateval defaultIconHorizontalMarginDp = 30@ColorResprivateval defaultRightIconTint = R.color.white_FFFFFF
@ColorResprivateval defaultLeftIconTint = R.color.white_FFFFFF
@ColorResprivateval defaultLeftBackgroundColor = R.color.red_FF7F8F
@ColorResprivateval defaultRightBackgroundColor = R.color.green_64ECA8
@ColorResprivateval defaultTextsColor = R.color.white_FFFFFF
privateval defaultLeftText = context.getString(R.string.action_left_swipe)
privateval defaultRightText = context.getString(R.string.action_right_swipe)
privateval defaultTextsTypeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
privateval defaultTextsSize = context.resources.getDimensionPixelSize(R.dimen.text_size_12px)
/* ------------------- MEMBERS ------------------- *//** Left and Right icons horizontal margin (left and right respectively) */var iconsHorizontalMargin: Int = 0set(value) {
field = context.applyDimension(value).toInt()
}
/** Left and Right icons size. The size is applied to the drawables is width == height (square) */var iconsSize: Int = 0set(value) {
field = context.applyDimension(value).toInt()
}
/** Helper member */privateval halfIconSize: Floatget() = iconsSize / 2f/** Left icon Drawable to draw */var leftIcon: Drawable? = ResourcesCompat.getDrawable(
context.resources, R.drawable.ic_left_swipe, context.theme
)?.mutate()
/** Right icon Drawable to draw */var rightIcon: Drawable? = ResourcesCompat.getDrawable(
context.resources, R.drawable.ic_right_swipe, context.theme
)?.mutate()
/** Left text String to draw. Positioned below leftIcon and centered horizontally. */var leftText: String = ""set(value) {
field = value.toUpperCase()
}
/** Right text String to draw. Positioned below rightIcon and centered horizontally. */var rightText: String = ""set(value) {
field = value.toUpperCase()
}
/** Right and Left texts typeface (font) */var textsTypeface: Typeface? = nullset(value) {
field = value
textPaint.typeface = value
}
/** Right and Left texts sizes */var textsSize: Float = 0fset(value) {
field = value
textPaint.textSize = value
}
/** Left and Right texts colors */@ColorResvar textsColor: Int = 0set(value) {
field = value
textPaint.color = ContextCompat.getColor(context, value)
}
/** Right icon [Drawable] tint color */@ColorResvar rightIconTint: Int = 0set(value) {
field = ContextCompat.getColor(context, value)
rightIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left icon [Drawable] tint color */@ColorResvar leftIconTint: Int = 0set(value) {
field = ContextCompat.getColor(context, value)
leftIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left View Background Color shown when the user is swiping */@ColorResvar leftBackgroundColor: Int = 0set(value) {
field = value
leftPaint.color = ContextCompat.getColor(context, value)
}
/** Right View Background Color shown when the user is swiping */@ColorResvar rightBackgroundColor: Int = 0set(value) {
field = value
rightPaint.color = ContextCompat.getColor(context, value)
}
/** false for swipe to left direction disabled */var swipeToLeftEnabled: Boolean = trueset(value) {
field = value
if (!value) {
setDefaultSwipeDirs(ItemTouchHelper.RIGHT)
}
}
/** false for swipe to right direction disabled */var swipeToRightEnabled: Boolean = trueset(value) {
field = value
if (!value) {
setDefaultSwipeDirs(ItemTouchHelper.LEFT)
}
}
/** Max Item Width for drawing when is being swiped [0 - 1] **/var swipeableWidthPercentage: Float = 0.25f/**
* Item swipe listener called when the user swipes the item to the left/right.
* Notified with the item position and direction.
*
* Values of direction: [ItemTouchHelper.LEFT] or [ItemTouchHelper.RIGHT]
*/var onItemSwipeListener: OnItemSwipeListener? = null/* ------------------- DRAWING ------------------- *//** Paint used to draw the left background */privateval leftPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the right background */privateval rightPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the left and right texts */privateval textPaint: Paint = Paint()
privatevar bgAlphaAnimation: ValueAnimator
privateval recyclerViewAdapterDataObserver: RecyclerView.AdapterDataObserver
privatevar pendingItemsAnimations: Int = 0init {
iconsSize = defaultIconSizeDp
iconsHorizontalMargin = defaultIconHorizontalMarginDp
leftText = defaultLeftText
rightText = defaultRightText
leftBackgroundColor = defaultLeftBackgroundColor
rightBackgroundColor = defaultRightBackgroundColor
textsTypeface = defaultTextsTypeface
textsSize = defaultTextsSize.toFloat()
textsColor = defaultTextsColor
rightIconTint = defaultRightIconTint
leftIconTint = defaultLeftIconTint
bgAlphaAnimation = ValueAnimator.ofInt(255, 255 / 2).apply {
interpolator = AccelerateDecelerateInterpolator()
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.REVERSE
duration = 500
}
ItemTouchHelper(this).attachToRecyclerView(recyclerView)
recyclerViewAdapterDataObserver = object : RecyclerView.AdapterDataObserver() {
overridefunonItemRangeChanged(positionStart: Int, itemCount: Int) {
pendingItemsAnimations -= 1if (pendingItemsAnimations == 0) {
bgAlphaAnimation.cancel()
bgAlphaAnimation.removeAllUpdateListeners()
leftPaint.alpha = 255
rightPaint.alpha = 255
}
}
}
recyclerView.adapter?.registerAdapterDataObserver(recyclerViewAdapterDataObserver)
}
overridefunonMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
returnfalse
}
overridefunonSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
pendingItemsAnimations += 1if (!bgAlphaAnimation.isRunning) {
bgAlphaAnimation.start()
}
onItemSwipeListener?.onItemSwiped(recyclerView, position, direction)
// recyclerView.adapter?.notifyItemChanged(position)
}
overridefunonChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
super.onChildDraw(c, recyclerView, viewHolder, dX * swipeableWidthPercentage, dY, actionState, isCurrentlyActive)
val view = viewHolder.itemView
val yCenter = (view.top + view.bottom) / 2when {
swipeToRightEnabled && dX > 0 -> {
// Swipe to the right
c.drawRect(
view.left.toFloat(),
view.top.toFloat(),
view.left.toFloat() + (dX * swipeableWidthPercentage),
view.bottom.toFloat(),
leftPaint
)
val leftTxtBounds = Rect()
textPaint.getTextBounds(leftText, 0, leftText.length, leftTxtBounds)
leftIcon?.let {
it.setBounds(
view.left + iconsHorizontalMargin,
yCenter - halfIconSize.toInt() - leftTxtBounds.height() / 2,
view.left + iconsHorizontalMargin + iconsSize,
yCenter + halfIconSize.toInt() - leftTxtBounds.height() / 2
)
it.draw(c)
}
val y = yCenter + halfIconSize + leftTxtBounds.height() / 2
c.drawText(
leftText,
iconsHorizontalMargin + halfIconSize - leftTxtBounds.width() / 2,
y,
textPaint
)
if (!isCurrentlyActive && abs(dX.toInt()) == view.width) {
bgAlphaAnimation.addUpdateListener {
leftPaint.alpha = it.animatedValue asInt
view.requestLayout()
}
}
}
swipeToLeftEnabled && dX < 0 -> {
// Swipe to the left
c.drawRect(
view.right.toFloat() + (dX * swipeableWidthPercentage),
view.top.toFloat(),
view.right.toFloat(),
view.bottom.toFloat(),
rightPaint
)
val rightTxtBounds = Rect()
textPaint.getTextBounds(rightText, 0, rightText.length, rightTxtBounds)
rightIcon?.let {
it.setBounds(
view.right - iconsHorizontalMargin - iconsSize,
yCenter - halfIconSize.toInt() - rightTxtBounds.height() / 2,
view.right - iconsHorizontalMargin,
yCenter + halfIconSize.toInt() - rightTxtBounds.height() / 2
)
it.draw(c)
}
val y = yCenter + halfIconSize + rightTxtBounds.height() / 2
c.drawText(
rightText,
view.right - iconsHorizontalMargin - halfIconSize - rightTxtBounds.width() / 2,
y,
textPaint
)
if (!isCurrentlyActive && abs(dX.toInt()) == view.width) {
bgAlphaAnimation.addUpdateListener {
rightPaint.alpha = it.animatedValue asInt
recyclerView.invalidate()
}
}
}
else -> {
// view unSwiped
}
}
}
interfaceOnItemSwipeListener{
funonItemSwiped(recyclerView: RecyclerView, position: Int, direction: Int)
}
}
fun Context.applyDimension(dimen: Int, unit: Int = TypedValue.COMPLEX_UNIT_DIP) =
TypedValue.applyDimension(
unit,
dimen.toFloat(),
resources.displayMetrics
)
The class is ready to use with customizable left/right icons and texts.
- When the user swipes an item to left/right, the background is animated with an alpha animation and the
OnItemSwipeListener
is called. - The
swipeableWidthPercentage
param is used to determine how much width we wan't to use for drawing (0.25 by default). This answers the question.
Example usage of the listener:
overridefunonItemSwiped(recyclerView: RecyclerView, position: Int, direction: Int) {
val door = adapter.peek(position)
when (direction) {
ItemTouchHelper.LEFT -> {
// TODO perform swipe to left action
recyclerView.postDelayed(
{
Toast.makeText(
requireContext(),
getString(R.string.swipe_left_action),
Toast.LENGTH_SHORT
).show()
// Reset the item to stop the animation and return to original state
adapter.notifyItemChanged(position)
},
2000
)
}
ItemTouchHelper.RIGHT -> {
// TODO perform swipe to right action
recyclerView.postDelayed(
{
Toast.makeText(
requireContext(),
getString(R.string.swipe_right_action),
Toast.LENGTH_SHORT
).show()
// Reset the item to stop the animation and return to original state
adapter.notifyItemChanged(position)
},
2000
)
}
}
}
Post a Comment for "Itemtouchhelper : Limit Swipe Width Of Itemtouchhelper.simplecallback On Recyclerview"