RecyclerView Item Not Moving When Using ItemTouchHelper

Nathan Sokalski 4,111 Reputation points
2021-06-02T05:27:54.073+00:00

I have a RecyclerView that uses an ItemTouchHelper. However, items do not move when I attempt to drag them. In my ItemTouchHelper, OnMove is called (and it does return true), and GetMovementFlags includes ActionStateDrag as part of it's MakeFlag. While I am attempting to drag the item that I am attempting to drag, the other items move as expected (the RecyclerView is used to allow the user to reorder the items), but the item being dragged does not move, although it is moved in the Adapter. Here is my OnMove method:

public override bool OnMove(RecyclerView rv, RecyclerView.ViewHolder dragged, RecyclerView.ViewHolder target)
{
    int draggedindex = dragged.AdapterPosition;
    int targetindex = target.AdapterPosition;
    if (targetindex < ((rv.GetAdapter() as PlayerNameInputAdapter).ItemCount - 1))
    {
        (rv.GetAdapter() as PlayerNameInputAdapter).Players.Move(draggedindex, targetindex);
        (rv.GetAdapter() as PlayerNameInputAdapter).NotifyItemMoved(draggedindex, targetindex);
    }
    return true;
}

After dragging has completed, the (not) dragged item remains in it's original position, although it is moved in the Adapter. What is the problem?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,377 questions
0 comments No comments
{count} votes

Accepted answer
  1. JarvanZhang 23,971 Reputation points
    2021-06-02T07:32:38.427+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    I created a basic demo to test the function, it works as expected. This problem will occur only when I comment the adapter.NotifyItemMoved line code. If I change the 'OnMove' method like below, it still works fine.

       public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)  
       {  
           if (viewHolder.ItemViewType != target.ItemViewType)  
           {  
               return false;  
           }  
         
           var adapter = recyclerView.GetAdapter();  
         
           // Notify the adapter of the move  
           adapter.NotifyItemMoved(viewHolder.AdapterPosition, target.AdapterPosition);  
           return true;  
       }  
    

    Here is the sample code, please check it to get the difference.

    MainActivity class

       public class MainActivity : AppCompatActivity, IOnStartDragListener  
       {  
           public static MainActivity Instance;  
           RecyclerView recyclerView;  
           RecyclerView.LayoutManager layoutManager;  
           public static ObservableCollection<TestModel> data;  
           private ItemTouchHelper _mItemTouchHelper;  
         
           protected override void OnCreate(Bundle savedInstanceState)  
           {  
               base.OnCreate(savedInstanceState);  
               Xamarin.Essentials.Platform.Init(this, savedInstanceState);  
               SetContentView(Resource.Layout.activity_main);  
         
               Instance = this;  
         
               recyclerView = FindViewById<RecyclerView>(Resource.Id.test_recyclerView);  
               layoutManager = new LinearLayoutManager(this);  
         
               data = new ObservableCollection<TestModel>();  
               data.Add(new TestModel { content = "item_1" });  
               data.Add(new TestModel { content = "item_2" });  
               data.Add(new TestModel { content = "item_3" });  
         
               TestAdapter testAdapter = new TestAdapter(data, this);  
               recyclerView.SetLayoutManager(layoutManager);  
               recyclerView.SetAdapter(testAdapter);  
         
               ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(testAdapter);  
               _mItemTouchHelper = new ItemTouchHelper(callback);  
               _mItemTouchHelper.AttachToRecyclerView(recyclerView);  
           }  
         
           public void OnStartDrag(RecyclerView.ViewHolder viewHolder)  
           {  
               _mItemTouchHelper.StartDrag(viewHolder);  
           }  
       }  
    

    ViewHolder and Adapter class

       public class TestViewHolder : RecyclerView.ViewHolder  
       {  
           public TextView ItemText { get; set; }  
           public TestViewHolder(View itemView) : base(itemView)  
           {  
               ItemText = itemView.FindViewById<TextView>(Resource.Id.itemText);  
           }  
       }  
         
       public class TestAdapter : RecyclerView.Adapter, ITemTouchHelperAdapter, IOnLongClickListener  
       {  
           public ObservableCollection<TestModel> list;  
           TestViewHolder viewHolder;  
           private readonly IOnStartDragListener dragStartListener;  
         
           public TestAdapter(ObservableCollection<TestModel> list, IOnStartDragListener dragStartListener)  
           {  
               this.list = list;  
               this.dragStartListener = dragStartListener;  
           }  
           public override int ItemCount  
           {  
               get  
               {  
                   return list.Count;  
               }  
           }  
           public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)  
           {  
               viewHolder = holder as TestViewHolder;  
               viewHolder.ItemText.Text = list[position].content;  
           }  
           public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)  
           {  
               View itemView = LayoutInflater.From(MainActivity.Instance).Inflate(Resource.Layout.recyclerView_itemLayout, parent, false);  
               TestViewHolder viewHolder = new TestViewHolder(itemView);  
               return viewHolder;  
           }  
           public bool OnItemMove(int fromPosition, int toPosition)  
           {  
               var tempPlanResource = list[fromPosition];  
               list[fromPosition] = list[toPosition];  
               list[toPosition] = tempPlanResource;  
               MainActivity.data = list;  
               NotifyItemMoved(fromPosition, toPosition);  
               return true;  
           }  
           public void OnItemDismiss(int position)  
           {  
               var item = list[position];  
               list.Remove(item);  
               NotifyItemRemoved(position);  
           }  
           public bool OnLongClick(View v)  
           {  
               dragStartListener.OnStartDrag(viewHolder);  
               return true;  
           }  
       }  
         
       public class TestModel  
       {  
           public string content { get; set; }  
       }  
    

    Related interfaces and touch callback class

       public interface IOnStartDragListener  
       {  
           void OnStartDrag(RecyclerView.ViewHolder viewHolder);  
       }  
         
       public interface ITemTouchHelperAdapter  
       {  
           bool OnItemMove(int fromPosition, int toPosition);  
           void OnItemDismiss(int position);  
       }  
         
       public class SimpleItemTouchHelperCallback : ItemTouchHelper.Callback  
       {  
           private readonly ITemTouchHelperAdapter _mAdapter;  
           public SimpleItemTouchHelperCallback(ITemTouchHelperAdapter adapter)  
           {  
               _mAdapter = adapter;  
           }  
           public override int GetMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)  
           {  
               const int dragFlags = ItemTouchHelper.Up | ItemTouchHelper.Down;  
               const int swipeFlags = ItemTouchHelper.ActionStateIdle;  
               return MakeMovementFlags(dragFlags, swipeFlags);  
           }  
           public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)  
           {  
               if (viewHolder.ItemViewType != target.ItemViewType)  
               {  
                   return false;  
               }  
               _mAdapter.OnItemMove(viewHolder.AdapterPosition, target.AdapterPosition);  
               return true;  
           }  
           public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction)  
           {  
               _mAdapter.OnItemDismiss(viewHolder.AdapterPosition);  
           }  
       }  
    

    Best Regards,

    Jarvan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.