MvxSmartRecyclerView 1.0.1
dotnet add package MvxSmartRecyclerView --version 1.0.1
NuGet\Install-Package MvxSmartRecyclerView -Version 1.0.1
<PackageReference Include="MvxSmartRecyclerView" Version="1.0.1" />
paket add MvxSmartRecyclerView --version 1.0.1
#r "nuget: MvxSmartRecyclerView, 1.0.1"
// Install MvxSmartRecyclerView as a Cake Addin #addin nuget:?package=MvxSmartRecyclerView&version=1.0.1 // Install MvxSmartRecyclerView as a Cake Tool #tool nuget:?package=MvxSmartRecyclerView&version=1.0.1
Android MvxSmartRecyclerView
This is an unofficial package that contains an expandable AndroidX RecyclerView supported for MvvmCross. This view allows us to bind a collection of items (objects, ViewModels, etc) to the ItemsSource
property. It works similarly to a RecyclerView. However, this comes with out-of-the-box functionality such as dragging items up and down and swiping them by setting a boolean
property to EnableDrag
, EnableSwipeRight
and/or EnableSwipeLeft
attributes.
All original functionality of MvxRecyclerView
is also available and it is highly encouraged that you read the documentation before proceeding.
Getting Started
You will need to ensure that you have the MvxSmartRecyclerView NuGet package installed in your .Droid
project.
- We want to create an app with a list of students. Firstly, in our
.Core
project, we will need to create our classes:Student.cs
andLesson.cs
.
public class Student : MvxNotifyPropertyChanged
{
private string firstName;
private string lastName;
private Lesson lesson;
private int sequence;
public Student(string firstName, string lastName, Lesson lesson)
{
FirstName = firstName;
LastName = lastName;
Lesson = lesson;
}
public string FirstName { get => firstName; set => SetProperty(ref firstName, value); }
public string LastName { get => lastName; set => SetProperty(ref lastName, value); }
public Lesson Lesson { get => lesson; set => SetProperty(ref lesson, value); }
public int Sequence { get => sequence; set => SetProperty(ref sequence, value); }
}
public class Lesson : MvxNotifyPropertyChanged
{
public static readonly Lesson Empty = new Lesson(Subject.None, DateTime.MinValue);
private int sequence;
public Lesson(Subject subject, DateTime dateTime)
{
Subject = subject;
DateTime = dateTime;
}
public DateTime DateTime { get; }
public int Sequence { get => sequence; set => SetProperty(ref sequence, value); }
public Subject Subject { get; }
}
public enum Subject
{
None = 0,
English = 1,
Math = 2,
}
- In our ViewModel we will initialise a list of
Student
s for binding.
public MvxObservableCollection<Student> Students { get; set; }
- For the rest of the steps, everything will be done in our
.Droid
project. We will create a layout to display ourStudent.cs
entity by creatingStudentView.xml
.
StudentView.xml
<?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="30dp"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:MvxBind="Text Format('{0} {1} - {2} on {3:d}', FirstName, LastName, Lesson.Subject, Lesson.DateTime);"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4sp"
android:textColor="?android:attr/colorAccent"
app:MvxBind="Text Sequence;"/>
</LinearLayout>
- Finally, adding
MvxSmartRecyclerView
to one of yourView.xml
is very simple.
<MvvmCross.SmartRecyclerView.MvxSmartRecyclerView
android:id="@+id/smart_appointment_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxItemTemplate="@layout/studentview"
app:MvxBind="ItemsSource Students;"/>
An alternative to using the MvxItemTemplate
attribute would be to create a custom Item Template Selector if you want each item to show a different view.
Custom Adapter
If you want to implement a custom adapter for MvxSmartRecyclerView
, you can either set it inside your View.cs
, or the recommended way: inside your View.xml
.
For example:
namespace AppointmentPlanner.DroidX.Components
{
public class AppointmentSmartRecyclerAdapter : MvxSmartRecyclerAdapter
{
public AppointmentSmartRecyclerAdapter()
{
}
public AppointmentSmartRecyclerAdapter(IMvxAndroidBindingContext bindingContext) : base(bindingContext)
{
}
// Custom code...
}
}
To set this adapter via xml
you will need to provide it in the MvxAdapter
attribute on the MvxSmartRecyclerView
. It must be of the format: Fully.Qualified.ClassName, Assembly.Name
. Hence, for the example above: let us say the assembly will be AppointmentPlanner.DroidX
and as you see the namespace is AppointmentPlanner.DroidX.Components
then the string will be: AppointmentPlanner.DroidX.Components.AppointmentSmartRecyclerAdapter, AppointmentPlanner.DroidX
.
<MvvmCross.SmartRecyclerView.MvxSmartRecyclerView
app:MvxAdapter="AppointmentPlanner.DroidX.Components.AppointmentSmartRecyclerAdapter, AppointmentPlanner.DroidX"/>
Note: if you do not provide a MvxAdapter
, the MvxSmartRecyclerView
will fallback to use MvxSmartRecyclerAdapter
.
Dragging Items
To enable the dragging feature, we need to modify our xml
and set the EnableDrag
attribute to true
.
<MvvmCross.SmartRecyclerView.MvxSmartRecyclerView
app:EnableItemDrag="true"/>
Prevent Dragging Items
If you want to prevent certain items in the list from being dragged, you can create a custom adapter that inherits MvxSmartRecyclerAdapter
and override the ShouldDragItem(object item)
method. This allows you to specify conditions on which items are allowed to be dragged.
public class AppointmentSmartRecyclerAdapter : MvxSmartRecyclerAdapter
{
// Constructors...
public override bool ShouldDragItem(object item)
{
if (item is something...)
{
// Allow dragging
return true;
}
else
{
// Prevent dragging
return false;
}
}
}
Swiping Items
To enable the swiping feature, we need to modify our xml
and set EnableSwipeRight
and/or EnableSwipeLeft
attributes to true
.
<MvvmCross.SmartRecyclerView.MvxSmartRecyclerView
app:EnableSwipeRight="true"
app:EnableSwipeLeft="true"
app:MvxBind="ItemSwipeRight SwipeRightCommand;
ItemSwipeLeft SwipeLeftCommand;"/>
Swipe actions are bindable and can have 2 different actions depending on the direction of the swipe. ItemSwipeLeft
and ItemSwipeRight
are bindable and are done in the same way as MvxRecyclerView
's ItemClickCommand
and ItemLongClickCommand
.
Prevent Swiping Items
If you want to prevent certain items in the list from being swiped left or right, you can create a custom adapter that inherits MvxSmartRecyclerAdapter
and override either the ShouldSwipeLeft(object item)
and/or ShouldSwipeRight(object item)
methods. This allows you to specify conditions on which items are allowed to be swiped left or right.
public class AppointmentSmartRecyclerAdapter : MvxSmartRecyclerAdapter
{
// Constructors...
public override bool ShouldSwipeLeft(object item)
{
if (item is something...)
{
// Allow left swipe
return true;
}
else
{
// Prevent left swipe
return false;
}
}
public override bool ShouldSwipeRight(object item)
{
if (item is something...)
{
// Allow right swipe
return true;
}
else
{
// Prevent right swipe
return false;
}
}
}
Swipe Backgrounds
We can show different backgrounds for an item depending on the swipe direction. In this example, we create 2 new layout files:
UnplanItemBackgroundView.xml
<?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:id="@id/smart_recycler_item_left_background_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="start"
android:background="@android:color/holo_blue_light">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginRight="10sp"
android:background="@drawable/abc_ic_ab_back_material"
android:contentDescription="Unplan Item" />
</LinearLayout>
RemoveItemBackgroundView.xml
<?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:id="@id/smart_recycler_item_right_background_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="end"
android:background="@android:color/holo_red_light">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10sp"
android:background="@drawable/abc_ic_clear_material"
android:contentDescription="Remove Item" />
</LinearLayout>
Important: the important thing to notice in these files is that each layout has an android:id
attribute. This is important for the control because it identifies which layout to show when swiping left or right, or not swiping at all. The android:id
s needed for the control are:
android:id="@id/smart_recycler_item_left_background_view"
(show layout when swiping right)android:id="@id/smart_recycler_item_right_background_view"
(show layout when swiping left)android:id="@id/smart_recycler_item_foreground_view"
(show layout for item when user is not swiping).
We then modify our StudentView.xml
to include these layouts and wrap everything in a FrameLayout
, making sure the background layouts are added first. We also need to add android:id="@id/smart_recycler_item_foreground_view"
to the nested LinearLayout
holding the default view to show when the user isn't swiping.
Note: by default, the LinearLayout
's background is transparent, resulting in the background views to show. The LinearLayout
's background
attribute is set to @android:color/white
to ensure the background views are hidden.
StudentView.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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">
<include
layout="@layout/unplanitembackgroundview"/>
<include
layout="@layout/removeitembackgroundview"/>
<LinearLayout
android:id="@id/smart_recycler_item_foreground_view"
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal"
android:gravity="center"
android:background="@android:color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:MvxBind="Text Format('{0} {1} - {2} on {3:d}', FirstName, LastName, Lesson.Subject, Lesson.DateTime);"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4sp"
android:textColor="?android:attr/colorAccent"
app:MvxBind="Text Sequence;"/>
</LinearLayout>
</FrameLayout>
Dynamic Swipe Backgrounds
If you want to show different swipe backgrounds depending on certain conditions relating to the item, you can create a custom adapter that inherits MvxSmartRecyclerAdapter
and override either the GetLeftBackgroundResourceId(object item)
and/or GetRightBackgroundResourceId(object item)
methods. This allows you to specify conditions on what backgrounds to show when swiping left or right.
You will also need to override the GetBackgroundResourceIds()
method to return a list of Resource Ids that will be used for the background views to help prevent visual bugs.
For example, continuing on from the Swipe Backgrounds
section above, we will create 2 more layouts:
PlanItemBackgroundView.xml
<?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:id="@+id/plan_item_background_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="start"
android:background="@android:color/holo_green_light">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginRight="10sp"
android:background="@drawable/abc_ic_ab_back_material"
android:contentDescription="Plan Item" />
</LinearLayout>
ResetItemBackgroundView.xml
<?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:id="@+id/reset_item_background_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="end"
android:background="@android:color/holo_orange_light">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10sp"
android:background="@drawable/abc_ic_clear_material"
android:contentDescription="Reset Item" />
</LinearLayout>
Important: make sure each layout has an android:id
attribute. This is important for the control because it will help identify which layout to show when swiping left or right. In this example, they are:
android:id="@+id/plan_item_background_view"
android:id="@+id/reset_item_background_view"
In our custom adapter we make sure to override the required methods: GetLeftBackgroundResourceId(object item)
, GetRightBackgroundResourceId(object item)
and GetBackgroundResourceIds()
:
public class AppointmentSmartRecyclerAdapter : MvxSmartRecyclerAdapter
{
// Constructors...
public override IEnumerable<int> GetBackgroundResourceIds() => new List<int>(2)
{
Resource.Id.reset_item_background_view,
Resource.Id.plan_item_background_view,
};
public override int GetLeftBackgroundResourceId(object item)
{
if (item is something...)
{
return Resource.Id.plan_item_background_view;
}
return base.GetLeftBackgroundResourceId(item);
}
public override int GetRightBackgroundResourceId(object item)
{
if (item is something...)
{
return Resource.Id.reset_item_background_view;
}
return base.GetRightBackgroundResourceId(item);
}
}
Note: the base implementation for:
GetLeftBackgroundResourceId(object item)
returnsResource.Id.smart_recycler_item_left_background_view
GetRightBackgroundResourceId(object item)
returnsResource.Id.smart_recycler_item_right_background_view
We then modify our StudentView.xml
to include these new layouts and wrap everything in a FrameLayout
, making sure the background layouts are added first.
StudentView.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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">
<include
layout="@layout/unplanitembackgroundview"/>
<include
layout="@layout/removeitembackgroundview"/>
<include
layout="@layout/planitembackgroundview"/>
<include
layout="@layout/resetitembackgroundview"/>
<LinearLayout
android:id="@id/smart_recycler_item_foreground_view"
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal"
android:gravity="center"
android:background="@android:color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:MvxBind="Text Format('{0} {1} - {2} on {3:d}', FirstName, LastName, Lesson.Subject, Lesson.DateTime);"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4sp"
android:textColor="?android:attr/colorAccent"
app:MvxBind="Text Sequence;"/>
</LinearLayout>
</FrameLayout>
Note: for the actual logic that gets executed when swiping left or right, the ItemSwipeLeft
and ItemSwipeRight
will need to have logic that accounts for this.
Performance Optimisation
If you want to fine-tune when the RecyclerView should update its views, you can create a custom adapter that inherits MvxSmartRecyclerAdapter
and override the CreateDiffUtilHelper(IList oldList, IList newList)
method. This method allows you to return a DiffUtil.Callback
object which helps the RecyclerView determine what views to update when performing an operation (add, remove, update).
By default, this method returns a MvxDefaultSmartDiffUtilHelper
which handles any object. However, you can override the method and return your own custom class that inherits MvxSmartDiffUtilHelper<T>
to fine-tune when the RecyclerView should update its views.
Using the list of students as an example:
public class AppoinmentDiffUtilHelper : MvxSmartDiffUtilHelper<Student>
{
public AppoinmentDiffUtilHelper(IList oldList, IList newList)
: base(oldList, newList)
{
}
protected override bool AreContentsTheSame(Student oldItem, Student newItem)
{
return oldItem.FirstName == newItem.FirstName
&& oldItem.LastName == newItem.LastName
&& oldItem.Lesson.Subject == newItem.Lesson.Subject
&& oldItem.Lesson.DateTime == newItem.Lesson.DateTime;
}
protected override bool AreItemsTheSame(Student oldItem, Student newItem)
{
return oldItem.FirstName == oldItem.FirstName
&& oldItem.LastName == newItem.LastName;
}
}
Note: the AreItemsTheSame(Student oldItem, Student newItem)
method checks if the two items represent the same item. If true
: the AreContentsTheSame(Student oldItem, Student newItem)
method is called and checks if the item's content has been changed. If true
: the RecyclerView updates the corresponding view, otherwise it doesn't.
In our custom adapter we make sure to override the CreateDiffUtilHelper(IList oldList, IList newList)
method and return a new instance of our custom DiffUtil.Callback
class: AppoinmentDiffUtilHelper
.
public class AppointmentSmartRecyclerAdapter : MvxSmartRecyclerAdapter
{
// Constructors...
protected override DiffUtil.Callback CreateDiffUtilHelper(IList oldList, IList newList) => new AppoinmentDiffUtilHelper(oldList, newList);
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
MonoAndroid | monoandroid10.0 is compatible. |
-
MonoAndroid 10.0
- MvvmCross.DroidX.RecyclerView (>= 7.0.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on MvxSmartRecyclerView:
Package | Downloads |
---|---|
MvxSectionedRecyclerView
MvvmCross is the .NET MVVM framework for cross-platform solutions, including Xamarin iOS, Xamarin Android, Xamarin Forms, Windows and Mac. This is an unofficial package that contains a sectioned AndroidX RecyclerView with out-of-the-box functionality such as dragging items up and down and swiping them, supported for MvvmCross. |
GitHub repositories
This package is not used by any popular GitHub repositories.