RecyclerView when dragging first item, it jumps to bottom
Asked Answered
M

2

7

I am using “drag & drop” and “swipe-to-dismiss” in RecyclerView with ItemTouchHelper. Without any library, according to this tutorial . It works perfectly. But the problem occurs when there is scroll in RecyclerView . When I start to drag first item down, first and second elements swap positions, RecyclerView is scrolled up and the first element(that I am dragging) occurs on bottom before last element(many swipes occure because of RecyclerView scrolling). It happens very fast, so I cannot put first element wherever I want.

Note that It happens only with first element, also only when dragging down.

I think this happens because when second element goes to position 1, third element goes to second position and also swipes with first element, and the same with other elements, they are all swiped with first element. How can I fix this.

This is what is called when first item changes position with second (and also with any dragging when items swap positions). After one swap, one item goes done:

    @Override
    public void onItemMove(int fromPosition, int toPosition) {
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(categories, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(categories, i, i - 1);
            }
        }

        notifyItemMoved(fromPosition, toPosition);
    }
Merrick answered 17/3, 2016 at 11:2 Comment(2)
Can you please share your codeQuarles
I have added the method that is called multiple times after first swap (1 and 2 element) because of scroll. Other code is from the tutorial in linkMerrick
R
3

I had a same problem with that tutorial. I searched about that problem and got the answer finally in this link. For me, second answer worked perfectly. I will upload the code which just connects the tutorial code and the second answer code on the link.

In the Adapter

    @Override
    public void onItemMove(int fromPosition, int toPosition) {
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(categories, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(categories, i, i - 1);
            }
        }

        // AdapterInteractionListener
        mListener.maintainRecyclerViewPosition(fromPosition, toPosition);
    }

In the Activity/Fragment

    LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
    int firstPos = layoutManager.findFirstCompletelyVisibleItemPosition();
    int offsetTop = 0;
    if(firstPos >= 0){
      View firstView = layoutManager.findViewByPosition(firstPos);
      offsetTop = layoutManager.getDecoratedTop(firstView) - layoutManager.getTopDecorationHeight(firstView);
    }

    mItemAdapter.notifyItemMoved(fromPosition, toPosition);
    if(firstPos >= 0) {
      layoutManager.scrollToPositionWithOffset(firstPos, offsetTop);
    }
Rheingold answered 29/1, 2019 at 7:33 Comment(1)
I am also facing this same issue. But this solution is not working for me. Any other idea please?Intro
S
3

Just return true in callback:

private val itemTouchHelper = object : ItemTouchHelper.Callback() {
        override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
            val drag = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END
            val swipe = 0
            return makeMovementFlags(drag, swipe)
        }

        override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
            val fromPosition = viewHolder.adapterPosition
            val toPosition = target.adapterPosition

            Collections.swap(systemsAdapter?.currentSystems, fromPosition, toPosition)
            recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)

            return true
        }

        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

        override fun isItemViewSwipeEnabled(): Boolean {
            return true
        }

        override fun isLongPressDragEnabled(): Boolean {
            return true
        }

    }
Siderite answered 30/10, 2020 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.