ReOrder the list items by drag and drop in xamarin android using RecyclerView

Brief:  Explanation with simple steps to how to re-order the list items by long press, drag and drop using recycler view in xamarin android.
  



In my previous post explained How to create image gallery control in xamarin form, How to encrypt sqlite database.

DescriptionIf you are looking for a requirement of re arranging the list items based on some priority then you are in the right place and here you can go through the example. Lets get in to the steps,

Step1: Create the new xamarin native project and add the below required packages.


1
2
Xamarin.Android.Support.v4
Xamarin.Android.Support.v7
On installation of above packages it will install all the required child packages also.

Step2: Create UI
Add new layout file one for recycler view and another one to dispay custom row item. 


ReOrderLayout.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginBottom="5dp"
android:layout_above="@+id/mLLButtonContainer">
<TextView
android:id="@+id/reorderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="1.Set the below items in correct order"
android:gravity="center_vertical"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="#ff000000"
android:background="@android:color/white"
android:maxLines="1"
android:ellipsize="end"
android:paddingTop="5dp"
android:paddingBottom="5dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="1dp"
android:background="@android:color/darker_gray"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/ResourceReorderRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp" />
</LinearLayout>
<TextView
android:id="@+id/mDone"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:textColor="@android:color/black"
android:text="Done"
android:maxLines="1"
android:ellipsize="end"
android:layout_gravity="center_horizontal"
android:paddingRight="40dp"
android:paddingLeft="40dp"
android:background="@android:color/darker_gray"
android:paddingTop="1dp"
android:paddingBottom="5dp"
android:textSize="16dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:layout_marginBottom="5dp" />
</LinearLayout>

In my example considered displaying quiz question with answer requiring to set the item in correct order.

CustomListItemReorder.axml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ReorderView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/transparent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:background="@android:color/transparent"
android:gravity="center_horizontal">
<TextView
android:id="@+id/mTVResourceName"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:layout_toLeftOf="@+id/mTvReorderIcon"
android:text="sample"
android:textColor="@android:color/black"
android:maxLines="1"
android:ellipsize="end"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:layout_marginLeft="8dp"
android:textSize="15dp"
android:layout_centerVertical="true" />
<ImageView
android:id="@+id/mTvReorderIcon"
android:layout_width="90dp"
android:layout_height="45dp"
android:paddingRight="10dp"
android:src="@drawable/icns_reorder2"
android:layout_alignParentRight="true"
android:clickable="false"
android:layout_centerVertical="true" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="3dp"
android:background="@android:color/darker_gray" />
</LinearLayout>
Step3: Add Interfaces
Need to add two interface to deifne touch and drag
1. IOnStartDragListener.cs   
 OnStartDrag : Called when a view is requesting a start of a drag and accepts viewHolder of the recycler view to drag.

using Android.Support.V7.Widget;
namespace NativeCodeLog.Droid
{
public interface IOnStartDragListener
{
void OnStartDrag(RecyclerView.ViewHolder viewHolder);
}
}
2. ITemTouchHelperAdapter.cs 
OnItemMove : Called when an item has been dragged far enough to trigger a move. This is called every time an item is shifted, and not at the end of a "drop" event. 
OnItemDismiss:Called when an item has been dismissed by a swipe

namespace NativeCodeLog.Droid
{
public interface ITemTouchHelperAdapter
{
/**
* @param fromPosition The start position of the moved item.
* @param toPosition Then resolved position of the moved item.
* @return True if the item was moved to the new adapter position.
*/
bool OnItemMove(int fromPosition, int toPosition);
/**
* @param position The position of the item dismissed.
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void OnItemDismiss(int position);
}
}
Step4: Add ItemTouchHelperCallback Class Create new class SimpleItemTouchHelperCallback by inheriting from ItemTouchHelper.Callback

Here overriden below three methods provided by the ItemTouchHelper.Callback base class
1.GetMovementFlags : To update the touch movement state 
2.OnMove : To Notify the adapter of the moved position
3.OnSwiped  : To Notify the adapter of the dismissal


SimpleItemTouchHelperCallback.cs
using Android.Support.V7.Widget.Helper;
using Android.Support.V7.Widget;
namespace NativeCodeLog.Droid
{
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;
}
// Notify the adapter of the move
_mAdapter.OnItemMove(viewHolder.AdapterPosition, target.AdapterPosition);
return true;
}
public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction)
{
// Notify the adapter of the dismissal
_mAdapter.OnItemDismiss(viewHolder.AdapterPosition);
}
}
public class ReOrderViewHolder : RecyclerView.ViewHolder
{
public LinearLayout ReorderView;
public ImageView ReorderIcon;
public TextView ResourceName;
public ReOrderViewHolder(View view) : base(view)
{
ResourceName = view.FindViewById<TextView>(Resource.Id.mTVResourceName);
ReorderView = view.FindViewById<LinearLayout>(Resource.Id.ReorderView);
ReorderIcon = view.FindViewById<ImageView>(Resource.Id.mTvReorderIcon);
}
}
}

Step5: Create Adapter and view holder Class
Here ReOrderAdapters implemented ITemTouchHelperAdapter for the above mentioned functionality and IOnLongClickListener
to listen to the long click event.

SimpleItemTouchHelperCallback.cs
using Android.Support.V7.Widget.Helper;
using Android.Support.V7.Widget;
namespace NativeCodeLog.Droid
{
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;
}
// Notify the adapter of the move
_mAdapter.OnItemMove(viewHolder.AdapterPosition, target.AdapterPosition);
return true;
}
public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction)
{
// Notify the adapter of the dismissal
_mAdapter.OnItemDismiss(viewHolder.AdapterPosition);
}
}
public class ReOrderViewHolder : RecyclerView.ViewHolder
{
public LinearLayout ReorderView;
public ImageView ReorderIcon;
public TextView ResourceName;
public ReOrderViewHolder(View view) : base(view)
{
ResourceName = view.FindViewById<TextView>(Resource.Id.mTVResourceName);
ReorderView = view.FindViewById<LinearLayout>(Resource.Id.ReorderView);
ReorderIcon = view.FindViewById<ImageView>(Resource.Id.mTvReorderIcon);
}
}
}

Step6:  Add Activity Class
Created the activity ReOrderActivity and implemeted interface IOnStartDragListener.

ReOrderActivity.cs
using System.Collections.ObjectModel;
using Android.App;
using Android.OS;
using Android.Support.V7.Widget;
using Android.Support.V7.Widget.Helper;
namespace NativeCodeLog.Droid
{
[Activity(Label = "ReOrderList")]
public class ReOrderActivity : Activity, IOnStartDragListener
{
private ItemTouchHelper _mItemTouchHelper;
public static ObservableCollection<string> ResourceList;
private RecyclerView _resourceReorderRecyclerView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ReOrderLayout);
GetCollection();
var resourceAdapter = new ReOrderAdapters(ResourceList, this);
// Initialize the recycler view.
_resourceReorderRecyclerView = FindViewById<RecyclerView>(Resource.Id.ResourceReorderRecyclerView);
_resourceReorderRecyclerView.SetLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.Vertical, false));
_resourceReorderRecyclerView.SetAdapter(resourceAdapter);
_resourceReorderRecyclerView.HasFixedSize = true;
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(resourceAdapter);
_mItemTouchHelper = new ItemTouchHelper(callback);
_mItemTouchHelper.AttachToRecyclerView(_resourceReorderRecyclerView);
}
public void OnStartDrag(RecyclerView.ViewHolder viewHolder)
{
_mItemTouchHelper.StartDrag(viewHolder);
}
//Added sample data record here
public void GetCollection()
{
ResourceList = new ObservableCollection<string>();
ResourceList.Add("OnPause()");
ResourceList.Add("OnStart()");
ResourceList.Add("OnCreate()");
}
}
}

Git link: https://github.com/suchithm/NativeCodeLog

Screen recording:

This is all about the implementation of reorder list in xamarin android using recycler view. Keep visiting for more exciting stuffs and follow us on FB Page.

7 comments:

  1. NotifyDataSetChanged() alone wont help in showing index of each item. you need to update custom layout text view with position and position based on OnItemMove()

    ReplyDelete
  2. Hello, thank you for this tutorial. I'm hoping you can help. I now have firebase realtime database set up with my app but now the drag and drop feature on my items no longer work.

    I can drag the items but they just hover over all the other items and when I start dragging an item the other items don't move to fill the space.

    Do you have any idea how I can fix this?

    Thanks

    ReplyDelete
    Replies
    1. Doesn't matter which database you used here. Just check OnMove method in SimpleItemTouchHelperCallback class something missed here.

      Delete
  3. Adding reordering items is currently one of my most challenging tasks on my current app, so hopefully your tutorial will help me. However, because my app uses Xamarin.Forms, one thing that I hope to do at some point is convert your code into a Custom Renderer. If you have any suggestions comments or suggestions for doing this, it would be appreciated (along with the already much appreciated tutorial!). Thanks!

    ReplyDelete
  4. I cannot find ReOrderAdapter.cs?? Where to fond it?

    ReplyDelete
    Replies
    1. You can refer it from here : https://github.com/suchithm/NativeCodeLog/blob/master/Droid/ReOrder/ReOrderAdapter.cs

      Delete