Android MvxRecyclerView
EditAvailable in Android Support RecyclerView, MvvmCross 4.
MvvmCross has a implementation of Android’s RecyclerView, which allows us to bind a collection of ViewModels to the ItemsSource
property. It works similarly to a ListView. However, a RecyclerView is out of the box a more resource friendly view, due to enforcing the use of the ViewHolder pattern, it also supports refreshing parts of the View rather than the invalidating the entire View. RecyclerView, although very efficient, it does not come with all the bells and whistles that a normal ListView comes with, such as built in item click events, highlighting selected row and more. Some of these we have covered for you and this article serves the purpose of the common uses of a MvxRecyclerView
.
Currently MvxRecyclerView
supports binding to the following properties:
- ItemsSource
- ItemClick
- ItemLongClick
Getting started
First you need to ensure that you have the MvvmCross.Droid.Support.V7.RecyclerView
NuGet package installed in your Application project.
Then adding your first MvxRecyclerView
is fairly simple. You add the widget in your layout like so.
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
...
local:MvxItemTemplate="@layout/item_template"
local:MvxTemplateSelector="Fully.Qualified.Name,Assembly.Name"
local:MvxBind="ItemsSource Items; ItemClick ItemClickCommand; ItemLongClick ItemLongClickCommand"
/>
You will need to bind a ItemsSource
and provide a item template or item template selector for it.
Note: If you don’t provide a item template or item template selector
MvxRecyclerView
will fall back to using aSimpleListItem1
, which is a built in Android Resource. It will also just callToString()
on your item that you are supplying.
Using an Item Template
An item template is simply a Android Layout with binding descriptions, which match the type of ViewModel you are providing in the ItemsSource. Using a Item Template is useful if you only have one type of ViewModel in your ItemsSource. An example of this could be a cell showing first and last name of your friends.
The ViewModel could look something as follows.
private string _firstName;
public string FirstName
{
get => _firstName;
set => SetProperty(ref _firstName, value);
}
private string _lastName;
public string LastName
{
get => _lastName;
set => SetProperty(ref _lastName, value);
}
The item template layout could then look as follows.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text FirstName" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="Text LastName" />
</LinearLayout>
If you call your layout item_contact
, then using it when filling out the MvxItemTemplate
attribute you will need to prefix it with @layout/
to indicate it is a Android layout like so.
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
local:MvxItemTemplate="@layout/item_contact"
... />
Using an Item Template Selector
An ItemTemplateSelector
is especially useful, when wanting to show different types of ViewModels in the same list and you want to present them with different views for each different type of ViewModel. The idea behind the ItemTemplateSelector
class is that you then do not have to extend the MvxRecyclerAdapter
, to provide different Views for different ViewModel types.
So let us say are trying to keep track of animals in a zoo and you want a different View for each group of Animal. So mammals have its own View, reptiles its own View and so on.
Assuming you have a ViewModel type for each of these group like: MammalViewModel, ReptileViewModel or some other way you could uniquely identify which View to present for the ViewModel, you could create a ItemTemplateSelector
which could help you achieve this.
To create your own ItemTemplateSelector
you must create a class implementing the IMvxTemplateSelector
interface, which has two very important methods. GetItemViewType(object forItemObject)
is used for the RecyclerView to determine how to recycle the Views. If you return 0
it will assume there is only one View type. Usually you would just return the layout id here. GetItemLayoutId(int fromViewType)
this method is used to provide the actual id of the layout you want to use for the View type.
Ensure you are returning something else than
0
fromGetItemViewType(object)
if you use multiple views in yourItemTemplateSelector
.
A small example:
namespace Zoo.App
{
public class AnimalTemplateSelector : IMvxTemplateSelector
{
public int ItemTemplateId { get; set; } // fallback ItemTemplateId
public int GetItemViewType(object item)
{
if (item is MammalViewModel)
return 1;
if (item is ReptileViewModel)
return 2;
if (item is BirdViewModel)
return 3;
return -1;
}
public int GetItemLayoutId(int viewType)
{
if (viewType == 1)
return Resource.Layout.item_mammal;
if (viewType == 2)
return Resource.Layout.item_reptile;
if (viewType == 3)
return Resource.Layout.item_bird;
return ItemTemplateId;
}
}
}
To use this ItemTemplateSelector
you will need to provide it in the MvxTemplateSelector
attribute on the MvxRecyclerView
. It must be of the format: Fully.Qualified.ClassName,Assembly.Name
. Hence, for the example above. Let us say the assembly will be Zoo.App.Droid
and as you see the namespace is Zoo.App
then the string will be: Zoo.App.AnimalTemplateSelector,Zoo.App.Droid
.
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
local:MvxTemplateSelector="Zoo.App.AnimalTemplateSelector,Zoo.App.Droid"
... />
The ItemTemplateId
property in your ItemTemplateSelector
will get overwritten if you provide both the MvxTemplateSelector
and MvxItemTemplate
, by the value in MvxItemTemplate
.
Note: If you do not provide a
MvxTemplateSelector
theMvxRecyclerAdapter
will fallback to useMvxDefaultItemTemplateSelector
.
ItemClick and ItemLongClick commands
ItemClick
and ItemLongClick
can be bound on a MvxRecyclerView
to execute a command when a specific item in the view is either clicked or long clicked.
This is similar to MvxListView
s SelectedItem
property you can bind to, although in this case you can also get the long click.
When you create your command, you can optionally get the ViewModel
bound in the ItemsSource
. Just be careful when doing so for an ItemsSource
containing multiple types.
The binding to your command would look as follows.
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
local:MvxBind="ItemClick ItemClickCommand; ItemLongClickCommand"
... />
Single ViewModel Items Sources
private MvxCommand<DogViewModel> _dogClickCommand;
public MvxCommand<DogViewModel> DogClickCommand => _dogClickCommand =
_dogClickCommand ?? new MvxCommand<DogViewModel>(OnDogClickCommand);
private void OnDogClickCommand(DogViewModel dog)
{
// write on dog clicked logic here
}
Multiple ViewModel Items Source
Given that DogViewModel
and CatViewModel
both derive from MammalViewModel
you could create your command as follows for a MvxRecyclerView
bound to an ItemsSource
containing DogViewModel
and CatViewModel
instances.
private MvxCommand<MammalViewModel> _itemClickCommand;
public MvxCommand<DogViewModel> ItemClickCommand => _itemClickCommand =
_itemClickCommand ?? new MvxCommand<DogViewModel>(OnItemClickCommand);
private void OnItemClickCommand(MammalViewModel animal)
{
// do common animal stuff here
if (animal is DogViewModel dog)
{
// do dog stuff
}
else if (animal is CatViewModel cat)
{
// do cat stuff
}
}
Alternatively you could go a level lower and just use object
instead of MammalViewModel
if you do not need to do common stuff with the MammalViewModel
.