扩展 RecyclerView 示例

基本 回收器视图示例中 介绍的基本应用实际上没有太大作用 - 它只是滚动并显示固定的照片项列表,以便于浏览。 在实际应用程序中,用户希望能够通过点击屏幕中的项目来与应用交互。 此外,基础数据源可以更改 (或由应用) 更改,并且显示内容必须与这些更改保持一致。 在以下部分中,你将了解如何处理项目单击事件,并在基础数据源更改时进行更新 RecyclerView

处理Item-Click事件

当用户触摸 中的 RecyclerView项时,将生成一个项单击事件,以通知应用所触摸的项。 此事件不是由 RecyclerView 生成,而是将项目视图 (包装在视图持有者中,) 检测触摸并将这些触摸报告为单击事件。

为了说明如何处理项目单击事件,以下步骤说明如何修改基本照片查看应用以报告用户已触摸的照片。 当示例应用中发生项目单击事件时,将发生以下顺序:

  1. 照片的 CardView 检测到项目单击事件并通知适配器。

  2. 适配器将包含项位置信息的事件 () 转发到活动的项单击处理程序。

  3. 活动的项单击处理程序响应项单击事件。

首先,将名为 的ItemClick事件处理程序成员添加到类定义:PhotoAlbumAdapter

public event EventHandler<int> ItemClick;

接下来,将项单击事件处理程序方法添加到 MainActivity。 此处理程序短暂显示一个 Toast,指示已触摸的照片项:

void OnItemClick (object sender, int position)
{
    int photoNum = position + 1;
    Toast.MakeText(this, "This is photo number " + photoNum, ToastLength.Short).Show();
}

接下来,需要一行代码来向 OnItemClick 注册处理程序 PhotoAlbumAdapter。 创建 后 PhotoAlbumAdapter ,可以立即执行此操作:

mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
mAdapter.ItemClick += OnItemClick;

在此基本示例中,处理程序注册在 main 活动的 OnCreate 方法中发生,但生产应用可能会在 中OnResume注册处理程序并注销它 OnPause - 有关详细信息,请参阅活动生命周期

PhotoAlbumAdapter 现在,当它收到项目单击事件时,将调用 OnItemClick 。 下一步是在适配器中创建引发此 ItemClick 事件的处理程序。 紧接在适配器的 ItemCount 方法OnClick之后添加以下方法 :

void OnClick (int position)
{
    if (ItemClick != null)
        ItemClick (this, position);
}

此方法 OnClick 是适配器的 侦听器 ,用于从项视图中单击项的事件。 在通过项视图的视图持有者) 向项视图 (注册此侦听器之前, PhotoViewHolder 必须修改构造函数以接受此方法作为附加参数,并注册 OnClick 到项视图 Click 事件。 下面是修改后的 PhotoViewHolder 构造函数:

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);
}

参数 itemView 包含对 CardView 用户触摸的 的引用。 请注意,视图持有者基类知道项的布局位置 (CardView) 它通过 LayoutPosition 属性) 表示 (,并且当发生项目单击事件时,此位置将传递给适配器 OnClick 的 方法。 修改适配器的 OnCreateViewHolder 方法,以将适配器的 OnClick 方法传递给视图持有者的构造函数:

PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);

现在,当你生成并运行示例照片查看应用时,点击屏幕中的照片将导致显示一个 Toast,其中报告了已触摸的照片:

点击照片卡片时显示的示例 Toast

此示例仅演示使用 实现事件处理程序的 RecyclerView一种方法。 可在此处使用的另一种方法是将事件放置在视图持有者上,并让适配器订阅这些事件。 如果示例照片应用提供了照片编辑功能,则 每个 CardView中的 将需要TextViewImageView单独的事件:触摸 TextView 将启动一个EditView对话框,允许用户编辑描述文字,触摸 ImageView 将启动一个允许用户裁剪或旋转照片的照片触摸工具。 根据应用的需求,必须设计处理和响应触摸事件的最佳方法。

为了演示如何 RecyclerView 在数据集更改时进行更新,可以修改示例照片查看应用,以随机选取数据源中的照片,并将其与第一张照片交换。 首先,将 “随机选取 ”按钮添加到示例照片应用的 Main.axml 布局中:

<?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>

接下来,在 main 活动的 OnCreate 方法末尾添加代码,以在布局中查找Random Pick按钮并向其附加处理程序:

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();
    }
};

点击“随机选取”按钮时,此处理程序将调用相册的 RandomSwap 方法。 方法 RandomSwap 随机交换一张照片与数据源中的第一张照片,然后返回随机交换的照片的索引。 使用此代码编译和运行示例应用时,点击 “随机选取 ”按钮不会导致显示更改, RecyclerView 因为 不知道数据源的更改。

若要在数据源更改后保持 RecyclerView 更新,必须修改 随机选取 单击处理程序,以便为集合中已更改的每个项调用适配器 NotifyItemChanged 的 方法 (在本例中,有两个项目已更改:第一张照片和交换的照片) 。 这会导致 RecyclerView 更新其显示,使其与数据源的新状态保持一致:

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);
    }
};

现在,点击“ 随机选取 ”按钮时, RecyclerView 会更新显示集合中更下方的照片已与集合中的第一张照片交换:

交换前的第一个屏幕截图,交换后的第二个屏幕截图

当然, NotifyDataSetChanged 可以调用 而不是对 NotifyItemChanged进行两次调用,但这样做将强制 RecyclerView 刷新整个集合,即使集合中只有两个项已更改。 调用 比调用 NotifyItemChangedNotifyDataSetChanged效率高得多。