我使用自定义 ItemTouchHelper 和 GestureDetector 在 RecyclerView 中实现了向右滑动手势。滑动手势按预期工作,我可以看到 UI 元素对右侧滑动做出反应。但是,我面临一个问题,即未注册滑动项目上的点击事件。
abstract class SwipeHelper(context: Context, recyclerView: RecyclerView) :
ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
companion object {
const val BUTTON_WIDTH = 250
}
private val recyclerView: RecyclerView = recyclerView
private var buttons: MutableList<UnderlayButton> = ArrayList()
private var rightButtons: MutableList<RightlayButton> = ArrayList()
private var gestureDetector: GestureDetectorCompat? = null
private var rightGestureDetector: GestureDetectorCompat? = null
private var swipedPos = -1
private var swipeThreshold = 0.5f
private val buttonsBuffer: MutableMap<Int, List<UnderlayButton>> = HashMap()
private val rightButtonsBuffer: MutableMap<Int, List<RightlayButton>> = HashMap()
private var recoverQueue: Queue<Int>? = null
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
for (button in buttons) {
if (button.onClick(e.x, e.y)) break
}
return true
}
}
private val gestureRightListener = object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
for (button in rightButtons) {
if (button.onClick(e.x, e.y)) break
}
return true
}
}
@SuppressLint("ClickableViewAccessibility")
private val onTouchListener = View.OnTouchListener { view, e ->
if (swipedPos < 0) return@OnTouchListener false
val point = Point(e.rawX.toInt(), e.rawY.toInt())
Log.i(TAG + INVOICE_FRAGMENT, "point === -> " + point)
val swipedViewHolder: RecyclerView.ViewHolder? =
recyclerView.findViewHolderForAdapterPosition(swipedPos)
val swipedItem: View = swipedViewHolder?.itemView ?: return@OnTouchListener false
val rect = Rect()
swipedItem.getGlobalVisibleRect(rect)
when (e.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP, MotionEvent.ACTION_MOVE -> {
if (rect.top < point.y && rect.bottom > point.y) {
Log.i(TAG + INVOICE_FRAGMENT, "rect -> " + rect.top)
Log.i(TAG + INVOICE_FRAGMENT, "rect bottom -> " + rect.bottom)
Log.i(TAG + INVOICE_FRAGMENT, "point y-> " + point.y)
gestureDetector?.onTouchEvent(e)
}
else {
recoverQueue?.add(swipedPos)
swipedPos = -1
recoverSwipedItem()
}
if (rect.top < point.x && rect.bottom > point.x)
rightGestureDetector?.onTouchEvent(e)
else {
recoverQueue?.add(swipedPos)
swipedPos = -1
recoverSwipedItem()
}
}
}
false
}
init {
gestureDetector = GestureDetectorCompat(context, gestureListener)
rightGestureDetector = GestureDetectorCompat(context, gestureRightListener)
recyclerView.setOnTouchListener(onTouchListener)
buttonsBuffer.clear()
rightButtonsBuffer.clear()
recoverQueue = LinkedList<Int>()
attachSwipe()
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val pos = viewHolder.adapterPosition
if (swipedPos != pos)
recoverQueue?.add(swipedPos)
swipedPos = pos
buttons = if (buttonsBuffer.containsKey(swipedPos))
buttonsBuffer[swipedPos] as MutableList<UnderlayButton>
else
ArrayList()
rightButtons = if (rightButtonsBuffer.containsKey(swipedPos))
rightButtonsBuffer[swipedPos] as MutableList<RightlayButton>
else
ArrayList()
buttonsBuffer.clear()
rightButtonsBuffer.clear()
swipeThreshold = 0.5f * buttons.size * BUTTON_WIDTH
swipeThreshold = 0.5f * rightButtons.size * BUTTON_WIDTH
recoverSwipedItem()
}
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
return swipeThreshold
}
override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
return 0.1f * defaultValue
}
override fun getSwipeVelocityThreshold(defaultValue: Float): Float {
return 5.0f * defaultValue
}
override fun onChildDraw(
c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean
) {
val pos = viewHolder.adapterPosition
var translationX = dX
val itemView = viewHolder.itemView
if (pos < 0) {
swipedPos = pos
return
}
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Left swipe
if (dX < 0) {
var buffer: List<UnderlayButton> = ArrayList()
if (!buttonsBuffer.containsKey(pos)) {
instantiateUnderlayButton(viewHolder, buffer as MutableList<UnderlayButton>)
buttonsBuffer[pos] = buffer
} else {
buffer = buttonsBuffer[pos] as List<UnderlayButton>
}
translationX = dX * buffer.size * BUTTON_WIDTH / itemView.width
drawButtons(c, itemView, buffer, pos, translationX)
}
// Right swipe
else if (dX > 0) {
var buffer: List<RightlayButton> = ArrayList()
if (!rightButtonsBuffer.containsKey(pos)) {
instantiateRightlayButton(viewHolder, buffer as MutableList<RightlayButton>)
rightButtonsBuffer[pos] = buffer
} else {
buffer = rightButtonsBuffer[pos] as List<RightlayButton>
}
translationX = dX * buffer.size * BUTTON_WIDTH / itemView.width
drawRightButtons(c, itemView, buffer, pos, translationX)
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive)
}
private fun recoverSwipedItem() {
while (recoverQueue!!.isNotEmpty()) {
val pos = recoverQueue?.poll()
if (pos != null) {
if (pos > -1) {
recyclerView.adapter?.notifyItemChanged(pos)
}
}
}
}
private fun drawButtons(
c: Canvas, itemView: View, buffer: List<UnderlayButton>,
pos: Int, dX: Float
) {
var right = itemView.right.toFloat()
val dButtonWidth = (-1) * dX / buffer.size
for (button in buffer) {
val left = right - dButtonWidth
button.onDraw(
c, RectF(
left,
itemView.top.toFloat(),
right,
itemView.bottom.toFloat()
), pos
)
right = left
}
}
private fun drawRightButtons(
c: Canvas, itemView: View, buffer: List<RightlayButton>,
pos: Int, dX: Float
) {
var right = itemView.left.toFloat()
val dButtonWidth = (-1) * dX / buffer.size
for (button in buffer) {
val left = right - dButtonWidth
button.onDraw(
c, RectF(
left,
itemView.top.toFloat(),
right,
itemView.bottom.toFloat()
), pos
)
right = left
}
}
fun attachSwipe() {
val itemTouchHelper = ItemTouchHelper(this)
itemTouchHelper.attachToRecyclerView(recyclerView)
}
abstract fun instantiateUnderlayButton(
viewHolder: RecyclerView.ViewHolder,
underlayButtons: MutableList<UnderlayButton>
)
abstract fun instantiateRightlayButton(
viewHolder: RecyclerView.ViewHolder,
rightlayButtons: MutableList<RightlayButton>
)
class RightlayButton(
private val text: String,
private val imageResId: Int,
private val color: Int,
private val clickListener: RightButtonClickListener
) {
private var pos = 0
private var clickRegion: RectF? = null
fun onClick(x: Float, y: Float): Boolean {
if (clickRegion != null && clickRegion!!.contains(x, y)) {
clickListener.onClick(pos)
return true
}
return false
}
fun onDraw(c: Canvas, rect: RectF, pos: Int) {
val p = Paint()
// Draw background
p.color = color
c.drawRect(rect, p)
// Draw Text
p.color = Color.WHITE
p.textSize = 36f
val r = Rect()
val cHeight = rect.height()
val cWidth = rect.width()
p.textAlign = Paint.Align.LEFT
p.getTextBounds(text, 0, text.length, r)
val x = cWidth / 2f - r.width() / 2f - r.left
val y = cHeight / 2f + r.height() / 2f - r.bottom
c.drawText(text, rect.left + x, rect.top + y, p)
clickRegion = rect
this.pos = pos
}
}
class UnderlayButton(
private val text: String,
private val imageResId: Int,
private val color: Int,
private val clickListener: UnderlayButtonClickListener
) {
private var pos = 0
private var clickRegion: RectF? = null
fun onClick(x: Float, y: Float): Boolean {
if (clickRegion != null && clickRegion!!.contains(x, y)) {
clickListener.onClick(pos)
return true
}
return false
}
fun onDraw(c: Canvas, rect: RectF, pos: Int) {
val p = Paint()
// Draw background
p.color = color
c.drawRect(rect, p)
// Draw Text
p.color = Color.WHITE
p.textSize = 36f
val r = Rect()
val cHeight = rect.height()
val cWidth = rect.width()
p.textAlign = Paint.Align.LEFT
p.getTextBounds(text, 0, text.length, r)
val x = cWidth / 2f - r.width() / 2f - r.left
val y = cHeight / 2f + r.height() / 2f - r.bottom
c.drawText(text, rect.left + x, rect.top + y, p)
clickRegion = rect
this.pos = pos
}
}
interface UnderlayButtonClickListener {
fun onClick(pos: Int)
}
interface RightButtonClickListener {
fun onClick(pos: Int)
}
}
我会考虑将 ItemTouchHelper 附加到您的 RecyclerView。
实现是这样的: 新的 ItemTouchHelper(新的 ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) { @覆盖 公共 boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) { 返回假; }
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (direction == ItemTouchHelper.RIGHT) {
onPreviousMonth();
} else if (direction == ItemTouchHelper.LEFT) {
onNextMonth();
}
}
}).attachToRecyclerView(recyclerView);
如果答案对您解决问题有帮助,请投票