右滑动手势已成功实现,但项目单击在 RecyclerView 中不起作用

问题描述 投票:0回答:1

我使用自定义 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)
}
}
  • 右侧滑动手势工作正常,触发了预期的 UI 更改。
  • 点击滑动项目时,GestureDetector 中的 onSingleTapConfirmed 方法不会被执行。
  • 我已经验证了UnderlayButton类中的点击事件逻辑是正确的。
android kotlin android-recyclerview swipe-gesture motionevent
1个回答
0
投票

我会考虑将 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);

如果答案对您解决问题有帮助,请投票

© www.soinside.com 2019 - 2024. All rights reserved.