Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The basic app described in
A Basic RecyclerView Example
actually doesn't do much – it simply scrolls and displays a fixed
list of photograph items to facilitate browsing. In real-world
applications, users expect to be able to interact with the app by
tapping items in the display. Also, the underlying data source can
change (or be changed by the app), and the contents of the display
must remain consistent with these changes. In the following sections,
you'll learn how to handle item-click events and update RecyclerView
when the underlying data source changes.
Handling Item-Click Events
When a user touches an item in the RecyclerView, an item-click
event is generated to notify the app as to which item was touched. This
event is not generated by RecyclerView – instead, the item
view (which is wrapped in the view holder) detects touches and reports
these touches as click events.
To illustrate how to handle item-click events, the following steps explain how the basic photo-viewing app is modified to report which photograph had been touched by the user. When an item-click event occurs in the sample app, the following sequence takes place:
The photograph's
CardViewdetects the item-click event and notifies the adapter.The adapter forwards the event (with item position information) to the activity's item-click handler.
The activity's item-click handler responds to the item-click event.
First, an event handler member called ItemClick is added to the
PhotoAlbumAdapter class definition:
public event EventHandler<int> ItemClick;
Next, an item-click event handler method is added to MainActivity.
This handler briefly displays a toast that indicates which photograph
item was touched:
void OnItemClick (object sender, int position)
{
int photoNum = position + 1;
Toast.MakeText(this, "This is photo number " + photoNum, ToastLength.Short).Show();
}
Next, a line of code is needed to register the OnItemClick handler
with PhotoAlbumAdapter. A good place to do this is immediately after
PhotoAlbumAdapter is created:
mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
mAdapter.ItemClick += OnItemClick;
In this basic example, handler registration takes place in the main
activity's OnCreate method, but a production app might
register the handler in OnResume and unregister it in OnPause
– see Activity Lifecycle
for more information.
PhotoAlbumAdapter will now call OnItemClick when it receives an item-click
event. The next step is to create a handler in the adapter that raises this
ItemClick event. The following method, OnClick, is added
immediately after the adapter's ItemCount method:
void OnClick (int position)
{
if (ItemClick != null)
ItemClick (this, position);
}
This OnClick method is the adapter's listener for item-click events
from item views. Before this listener can be registered with an item
view (via the item view's view holder), the PhotoViewHolder
constructor must be modified to accept this method as an additional
argument, and register OnClick with the item view Click event.
Here's the modified PhotoViewHolder constructor:
public PhotoViewHolder (View itemView, Action<int> listener)
: base (itemView)
{
Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
itemView.Click += (sender, e) => listener (base.LayoutPosition);
}
The itemView parameter contains a reference to the CardView that
was touched by the user. Note that the view holder base class knows the
layout position of the item (CardView) that it represents (via the
LayoutPosition property), and this position is passed to the
adapter's OnClick method when an item-click event takes place. The
adapter's OnCreateViewHolder method is modified to pass the adapter's
OnClick method to the view-holder's constructor:
PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);
Now when you build and run the sample photo-viewing app, tapping a photo in the display will cause a toast to appear that reports which photograph was touched:
This example demonstrates just one approach for implementing event
handlers with RecyclerView. Another approach that could be used
here is to place events on the view holder and have the adapter
subscribe to these events. If the sample photo app provided a photo
editing capability, separate events would be required for the
ImageView and the TextView within each CardView: touches on the
TextView would launch an EditView dialog that lets the user edit
the caption, and touches on the ImageView would launch a photo
touchup tool that lets the user crop or rotate the photo. Depending on
the needs of your app, you must design the best approach for handling
and responding to touch events.
To demonstrate how RecyclerView can be updated when the data set
changes, the sample photo-viewing app can be modified to randomly pick
a photo in the data source and swap it with the first photo. First,
a Random Pick button is added to the example photo app's
Main.axml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/randPickButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Random Pick" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:scrollbars="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Next, code is added at the end of the main activity's OnCreate
method to locate the Random Pick button in the layout and attach a
handler to it:
Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);
randomPickBtn.Click += delegate
{
if (mPhotoAlbum != null)
{
// Randomly swap a photo with the first photo:
int idx = mPhotoAlbum.RandomSwap();
}
};
This handler calls the photo album's RandomSwap method when the
Random Pick button is tapped. The RandomSwap method randomly
swaps a photo with the first photo in the data source, then returns the
index of the randomly-swapped photo. When you compile and run the sample
app with this code, tapping the Random Pick button does not result
in a display change because the RecyclerView is not aware of the
change to the data source.
To keep RecyclerView updated after the data source changes,
the Random Pick click handler must be modified to
call the adapter's NotifyItemChanged method for each item in the
collection that has changed (in this case, two items have changed: the
first photo and the swapped photo). This causes RecyclerView to
update its display so that it is consistent with the new state of the
data source:
Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);
randomPickBtn.Click += delegate
{
if (mPhotoAlbum != null)
{
int idx = mPhotoAlbum.RandomSwap();
// First photo has changed:
mAdapter.NotifyItemChanged(0);
// Swapped photo has changed:
mAdapter.NotifyItemChanged(idx);
}
};
Now, when the Random Pick button is tapped, RecyclerView updates
the display to show that a photo further down in the collection has
been swapped with the first photo in the collection:
Of course, NotifyDataSetChanged could have been called instead of
making the two calls to NotifyItemChanged, but doing so would force
RecyclerView to refresh the entire collection even though only two
items in the collection had changed. Calling NotifyItemChanged is
significantly more efficient than calling NotifyDataSetChanged.

