ItemTouchHelper:在 RecyclerView 上限制 ItemTouchHelper.SimpleCallBack 的滑动宽度
ItemTouchHelper : Limit swipe width of ItemTouchHelper.SimpleCallBack on RecyclerView
我已经成功地实现了滑动行为并用它执行了一些操作。我现在遇到的问题是我想在滑动项目时限制滑动宽度。
目前是这样
但我想将滑动行为宽度限制到这里
这是我到目前为止所做的:-
private void swipeExample(){
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
// final int fromPos = viewHolder.getAdapterPosition();
// final int toPos = target.getAdapterPosition();
return false;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int swipeDir) {
// Show delete confirmation if swipped left
if (swipeDir == ItemTouchHelper.LEFT) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Are you sure you want to delete?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteItem(viewHolder);
// getActivity().finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
} else if (swipeDir == ItemTouchHelper.RIGHT) {
// Show edit dialog
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Bitmap icon;
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
View itemView = viewHolder.itemView;
float height = (float) itemView.getBottom() - (float) itemView.getTop();
float width = height / 3;
if (dX > 0) {
p.setColor(Color.parseColor("#388E3C"));
RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom());
c.drawRect(background, p);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_edit_white_24dp);
RectF icon_dest = new RectF((float) itemView.getLeft() + width, (float) itemView.getTop() + width, (float) itemView.getLeft() + 2 * width, (float) itemView.getBottom() - width);
c.drawBitmap(icon, null, icon_dest, p);
} else if (dX < 0) {
p.setColor(Color.parseColor("#D32F2F"));
RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
c.drawRect(background, p);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white_24dp);
RectF icon_dest = new RectF((float) itemView.getRight() - 2 * width, (float) itemView.getTop() + width, (float) itemView.getRight() - width, (float) itemView.getBottom() - width);
c.drawBitmap(icon, null, icon_dest, p);
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
请问如何限制onChildDraw()
方法的宽度
提前致谢。
你的 onChildDraw()
方法最后调用它的 super super 方法如下:
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
要限制滑动的最大宽度,您可以调用 super.onChildDraw()
减少 dX
:
super.onChildDraw(c, recyclerView, viewHolder, dX / 4, dY, actionState, isCurrentlyActive);
根据 Alexander Thiel 的回复,我创建了一个 class 来管理左右项目滑动:
/**
* 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
*
*/
class ItemTouchSwipeHelperCallback(
private val recyclerView: RecyclerView
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.ACTION_STATE_IDLE,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
private val context: Context
get() = recyclerView.context
/* ------------------- VIEW DEFAULTS ------------------- */
private val defaultIconSizeDp = 28
private val defaultIconHorizontalMarginDp = 30
@ColorRes
private val defaultRightIconTint = R.color.white_FFFFFF
@ColorRes
private val defaultLeftIconTint = R.color.white_FFFFFF
@ColorRes
private val defaultLeftBackgroundColor = R.color.red_FF7F8F
@ColorRes
private val defaultRightBackgroundColor = R.color.green_64ECA8
@ColorRes
private val defaultTextsColor = R.color.white_FFFFFF
private val defaultLeftText = context.getString(R.string.action_left_swipe)
private val defaultRightText = context.getString(R.string.action_right_swipe)
private val defaultTextsTypeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
private val defaultTextsSize = context.resources.getDimensionPixelSize(R.dimen.text_size_12px)
/* ------------------- MEMBERS ------------------- */
/** Left and Right icons horizontal margin (left and right respectively) */
var iconsHorizontalMargin: Int = 0
set(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 = 0
set(value) {
field = context.applyDimension(value).toInt()
}
/** Helper member */
private val halfIconSize: Float
get() = 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? = null
set(value) {
field = value
textPaint.typeface = value
}
/** Right and Left texts sizes */
var textsSize: Float = 0f
set(value) {
field = value
textPaint.textSize = value
}
/** Left and Right texts colors */
@ColorRes
var textsColor: Int = 0
set(value) {
field = value
textPaint.color = ContextCompat.getColor(context, value)
}
/** Right icon [Drawable] tint color */
@ColorRes
var rightIconTint: Int = 0
set(value) {
field = ContextCompat.getColor(context, value)
rightIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left icon [Drawable] tint color */
@ColorRes
var leftIconTint: Int = 0
set(value) {
field = ContextCompat.getColor(context, value)
leftIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left View Background Color shown when the user is swiping */
@ColorRes
var leftBackgroundColor: Int = 0
set(value) {
field = value
leftPaint.color = ContextCompat.getColor(context, value)
}
/** Right View Background Color shown when the user is swiping */
@ColorRes
var rightBackgroundColor: Int = 0
set(value) {
field = value
rightPaint.color = ContextCompat.getColor(context, value)
}
/** false for swipe to left direction disabled */
var swipeToLeftEnabled: Boolean = true
set(value) {
field = value
if (!value) {
setDefaultSwipeDirs(ItemTouchHelper.RIGHT)
}
}
/** false for swipe to right direction disabled */
var swipeToRightEnabled: Boolean = true
set(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 */
private val leftPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the right background */
private val rightPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the left and right texts */
private val textPaint: Paint = Paint()
private var bgAlphaAnimation: ValueAnimator
private val recyclerViewAdapterDataObserver: RecyclerView.AdapterDataObserver
private var pendingItemsAnimations: Int = 0
init {
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() {
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
pendingItemsAnimations -= 1
if (pendingItemsAnimations == 0) {
bgAlphaAnimation.cancel()
bgAlphaAnimation.removeAllUpdateListeners()
leftPaint.alpha = 255
rightPaint.alpha = 255
}
}
}
recyclerView.adapter?.registerAdapterDataObserver(recyclerViewAdapterDataObserver)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
pendingItemsAnimations += 1
if (!bgAlphaAnimation.isRunning) {
bgAlphaAnimation.start()
}
onItemSwipeListener?.onItemSwiped(recyclerView, position, direction)
// recyclerView.adapter?.notifyItemChanged(position)
}
override fun onChildDraw(
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) / 2
when {
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 as Int
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 as Int
recyclerView.invalidate()
}
}
}
else -> {
// view unSwiped
}
}
}
interface OnItemSwipeListener {
fun onItemSwiped(recyclerView: RecyclerView, position: Int, direction: Int)
}
}
fun Context.applyDimension(dimen: Int, unit: Int = TypedValue.COMPLEX_UNIT_DIP) =
TypedValue.applyDimension(
unit,
dimen.toFloat(),
resources.displayMetrics
)
class 可以与可自定义的 left/right 图标和文本一起使用。
- 当用户将项目滑动到 left/right 时,背景会使用 alpha 动画并调用
OnItemSwipeListener
。
swipeableWidthPercentage
参数用于确定我们不想用于绘图的宽度(默认为 0.25)。这回答了这个问题。
侦听器的用法示例:
override fun onItemSwiped(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
)
}
}
}
我已经成功地实现了滑动行为并用它执行了一些操作。我现在遇到的问题是我想在滑动项目时限制滑动宽度。
目前是这样
但我想将滑动行为宽度限制到这里
这是我到目前为止所做的:-
private void swipeExample(){
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
// final int fromPos = viewHolder.getAdapterPosition();
// final int toPos = target.getAdapterPosition();
return false;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int swipeDir) {
// Show delete confirmation if swipped left
if (swipeDir == ItemTouchHelper.LEFT) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Are you sure you want to delete?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteItem(viewHolder);
// getActivity().finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
} else if (swipeDir == ItemTouchHelper.RIGHT) {
// Show edit dialog
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Bitmap icon;
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
View itemView = viewHolder.itemView;
float height = (float) itemView.getBottom() - (float) itemView.getTop();
float width = height / 3;
if (dX > 0) {
p.setColor(Color.parseColor("#388E3C"));
RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom());
c.drawRect(background, p);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_edit_white_24dp);
RectF icon_dest = new RectF((float) itemView.getLeft() + width, (float) itemView.getTop() + width, (float) itemView.getLeft() + 2 * width, (float) itemView.getBottom() - width);
c.drawBitmap(icon, null, icon_dest, p);
} else if (dX < 0) {
p.setColor(Color.parseColor("#D32F2F"));
RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
c.drawRect(background, p);
icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white_24dp);
RectF icon_dest = new RectF((float) itemView.getRight() - 2 * width, (float) itemView.getTop() + width, (float) itemView.getRight() - width, (float) itemView.getBottom() - width);
c.drawBitmap(icon, null, icon_dest, p);
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
请问如何限制onChildDraw()
方法的宽度
提前致谢。
你的 onChildDraw()
方法最后调用它的 super super 方法如下:
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
要限制滑动的最大宽度,您可以调用 super.onChildDraw()
减少 dX
:
super.onChildDraw(c, recyclerView, viewHolder, dX / 4, dY, actionState, isCurrentlyActive);
根据 Alexander Thiel 的回复,我创建了一个 class 来管理左右项目滑动:
/**
* 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
*
*/
class ItemTouchSwipeHelperCallback(
private val recyclerView: RecyclerView
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.ACTION_STATE_IDLE,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
private val context: Context
get() = recyclerView.context
/* ------------------- VIEW DEFAULTS ------------------- */
private val defaultIconSizeDp = 28
private val defaultIconHorizontalMarginDp = 30
@ColorRes
private val defaultRightIconTint = R.color.white_FFFFFF
@ColorRes
private val defaultLeftIconTint = R.color.white_FFFFFF
@ColorRes
private val defaultLeftBackgroundColor = R.color.red_FF7F8F
@ColorRes
private val defaultRightBackgroundColor = R.color.green_64ECA8
@ColorRes
private val defaultTextsColor = R.color.white_FFFFFF
private val defaultLeftText = context.getString(R.string.action_left_swipe)
private val defaultRightText = context.getString(R.string.action_right_swipe)
private val defaultTextsTypeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
private val defaultTextsSize = context.resources.getDimensionPixelSize(R.dimen.text_size_12px)
/* ------------------- MEMBERS ------------------- */
/** Left and Right icons horizontal margin (left and right respectively) */
var iconsHorizontalMargin: Int = 0
set(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 = 0
set(value) {
field = context.applyDimension(value).toInt()
}
/** Helper member */
private val halfIconSize: Float
get() = 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? = null
set(value) {
field = value
textPaint.typeface = value
}
/** Right and Left texts sizes */
var textsSize: Float = 0f
set(value) {
field = value
textPaint.textSize = value
}
/** Left and Right texts colors */
@ColorRes
var textsColor: Int = 0
set(value) {
field = value
textPaint.color = ContextCompat.getColor(context, value)
}
/** Right icon [Drawable] tint color */
@ColorRes
var rightIconTint: Int = 0
set(value) {
field = ContextCompat.getColor(context, value)
rightIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left icon [Drawable] tint color */
@ColorRes
var leftIconTint: Int = 0
set(value) {
field = ContextCompat.getColor(context, value)
leftIcon?.let {
DrawableCompat.setTint(it, field)
}
}
/** Left View Background Color shown when the user is swiping */
@ColorRes
var leftBackgroundColor: Int = 0
set(value) {
field = value
leftPaint.color = ContextCompat.getColor(context, value)
}
/** Right View Background Color shown when the user is swiping */
@ColorRes
var rightBackgroundColor: Int = 0
set(value) {
field = value
rightPaint.color = ContextCompat.getColor(context, value)
}
/** false for swipe to left direction disabled */
var swipeToLeftEnabled: Boolean = true
set(value) {
field = value
if (!value) {
setDefaultSwipeDirs(ItemTouchHelper.RIGHT)
}
}
/** false for swipe to right direction disabled */
var swipeToRightEnabled: Boolean = true
set(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 */
private val leftPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the right background */
private val rightPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
/** Paint used to draw the left and right texts */
private val textPaint: Paint = Paint()
private var bgAlphaAnimation: ValueAnimator
private val recyclerViewAdapterDataObserver: RecyclerView.AdapterDataObserver
private var pendingItemsAnimations: Int = 0
init {
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() {
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
pendingItemsAnimations -= 1
if (pendingItemsAnimations == 0) {
bgAlphaAnimation.cancel()
bgAlphaAnimation.removeAllUpdateListeners()
leftPaint.alpha = 255
rightPaint.alpha = 255
}
}
}
recyclerView.adapter?.registerAdapterDataObserver(recyclerViewAdapterDataObserver)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
pendingItemsAnimations += 1
if (!bgAlphaAnimation.isRunning) {
bgAlphaAnimation.start()
}
onItemSwipeListener?.onItemSwiped(recyclerView, position, direction)
// recyclerView.adapter?.notifyItemChanged(position)
}
override fun onChildDraw(
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) / 2
when {
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 as Int
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 as Int
recyclerView.invalidate()
}
}
}
else -> {
// view unSwiped
}
}
}
interface OnItemSwipeListener {
fun onItemSwiped(recyclerView: RecyclerView, position: Int, direction: Int)
}
}
fun Context.applyDimension(dimen: Int, unit: Int = TypedValue.COMPLEX_UNIT_DIP) =
TypedValue.applyDimension(
unit,
dimen.toFloat(),
resources.displayMetrics
)
class 可以与可自定义的 left/right 图标和文本一起使用。
- 当用户将项目滑动到 left/right 时,背景会使用 alpha 动画并调用
OnItemSwipeListener
。 swipeableWidthPercentage
参数用于确定我们不想用于绘图的宽度(默认为 0.25)。这回答了这个问题。
侦听器的用法示例:
override fun onItemSwiped(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
)
}
}
}