I have a recycler view with ItemTouchHelper. It allows dragging the items.
I want to limit the dragging to the bounds of recycler view - i.e. you can't just drag the view outside the container, so that it disappears.
I tried checking the absolute coordinates like this:
 @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);
...
}
but then I run into kinda rendering concurency - if I move the view slowly, it will stop moving when going out of bounds, but if I move faster - then it anyway leaves the recycler view bounds.
Any ideas / approaches?
The dY and dX value must be clipped to the RecyclerView's bounds:
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
    }
}
I adjusted user1185087's answer so that instead of teleporting your item, it simply won't handle it if the drag does go out.
@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);
    }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With