繁体   English   中英

ItemTouchHelper:防止越界拖动

[英]ItemTouchHelper: Prevent out of bounds dragging

我有一个带有 ItemTouchHelper 的回收器视图。 它允许拖动项目。

我想将拖动限制在回收器视图的范围内 - 即您不能将视图拖到容器外,使其消失。

我试着像这样检查绝对坐标:

 @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        recyclerView.getLocationOnScreen(pos);
        int rvY = pos[1];
        viewHolder.itemView.getLocationOnScreen(pos);
        int vhY = pos[1];


        if (rvY > vhY || rvY + recyclerView.getHeight() < vhY + viewHolder.itemView.getHeight()) {
            return;
        }
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
...
}

但是后来我遇到了渲染并发性 - 如果我缓慢移动视图,它会在越界时停止移动,但是如果我移动得更快 - 那么它无论如何都会离开回收器视图边界。

任何想法/方法?

dY 和 dX 值必须剪裁到RecyclerView的边界:

override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
    val clippedDx  = clip(recyclerView.width, viewHolder.itemView.left, viewHolder.itemView.right, dX)
    val clippedDy  = clip(recyclerView.height, viewHolder.itemView.top, viewHolder.itemView.bottom, dY)
    super.onChildDraw(c, recyclerView, viewHolder, clippedDx, clippedDy, actionState, isCurrentlyActive)
}

private fun clip(size: Int, start: Int, end: Int, delta: Float): Float {
    val newStart = start + delta
    val newEnd = end + delta

    val oobStart = 0 - newStart
    val oobEnd = newEnd - size

    return when {
        oobStart > 0 -> delta + oobStart
        oobEnd > 0 -> delta - oobEnd
        else -> delta
    }
}

我调整了 user1185087 的答案,这样它就不会传送您的项目,如果拖动确实消失,它就不会处理它。

@Override
public void onChildDraw(@NotNull Canvas c, @NotNull RecyclerView recyclerView,
                        @NotNull RecyclerView.ViewHolder viewHolder, float dX, float dY,
                        int actionState, boolean isCurrentlyActive) {
    float topY = viewHolder.itemView.getTop() + dY;
    float bottomY = topY + viewHolder.itemView.getHeight();

    // Only redraw child if it is inbounds of view
    if (topY > 0 && bottomY < recyclerView.getHeight()){
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
}

我有同样的问题。 所有答案都可以帮助我找到更好的方法而没有滞后。 :)

如果仅使用dY ,则会产生滞后。 所以我在onChildDraw 中itemView添加了位置确定。

override fun onChildDraw(c: Canvas, recyclerView: RecyclerView,
                         viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float,
                         actionState: Int, isCurrentlyActive: Boolean) {
    var edgeY = dY

    if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
        val lastPosition = (recyclerView.adapter?.itemCount ?: 0) - 1

        val view = viewHolder.itemView
        val pieceHeight = view.height / PIECE_RATIO

        val topEdge = recyclerView.top - pieceHeight
        val bottomEdge = recyclerView.height - view.bottom + pieceHeight

        if (movePosition == 0 && dY < topEdge) {
            edgeY = topEdge
        } else if (movePosition == lastPosition && dY > bottomEdge) {
            edgeY = bottomEdge
        }
    }

    super.onChildDraw(c, recyclerView, viewHolder, dX, edgeY, actionState, isCurrentlyActive)
}
  • PIECE_RATIO - 可以查看多远。
  • movePosition - 拖动viewHolder 的当前位置。 需要最佳性能。 您需要在onSelectedChangedonMove 中设置它。 清除clearView内的变量

代码:

/**
 * Variable need for best performance and more productive, because
 * get [RecyclerView.ViewHolder.getAdapterPosition] inside [onChildDraw]
 * is hard calculating operation
 */
protected var movePosition = RecyclerView.NO_POSITION

override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
    super.onSelectedChanged(viewHolder, actionState)

    /**
     * Get position on drag start
     */
    if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
        movePosition = viewHolder?.adapterPosition ?: RecyclerView.NO_POSITION
    }
}

override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
    super.clearView(recyclerView, viewHolder)

    /**
     * Clear position on drag end
     */
    movePosition = RecyclerView.NO_POSITION
}

override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder): Boolean {
    /**
     * Get position on drag
     */
    movePosition = target.adapterPosition
    return false
}

子类可以像这样使用movePosition

override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder): Boolean {
    super.onMove(recyclerView, viewHolder, target)
    return callback.onTouchMove(viewHolder.adapterPosition, movePosition)
}

您可以在链接中找到的所有源代码和示例: github 项目

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM