基本 RecyclerView 示例

为了了解 RecyclerView 典型应用程序的工作原理,本主题探讨 RecyclerViewer 示例应用,这是一个简单的代码示例,用于 RecyclerView 显示大量照片:

使用 CardView 显示照片的 RecyclerView 应用的两个屏幕截图

RecyclerViewer 使用 CardView 实现布局中的每个 RecyclerView 照片项。 由于 RecyclerView的性能优势,此示例应用能够顺利地快速滚动浏览大量照片,而不会有明显的延迟。

示例数据源

在此示例应用中,“相册”数据源 (类 PhotoAlbum 表示,) 提供 RecyclerView 项目内容。 PhotoAlbum 是带标题的照片集合;实例化时,你将获得 32 张照片的现成集合:

PhotoAlbum mPhotoAlbum = new PhotoAlbum ();

中的每个PhotoAlbum照片实例都公开属性,这些属性允许你读取其图像资源 ID PhotoID及其描述文字字符串 Caption。 对照片集合进行组织,以便每张照片都可以由索引器访问。 例如,以下代码行访问集合中第十张照片的图像资源 ID 和描述文字:

int imageId = mPhotoAlbum[9].ImageId;
string caption = mPhotoAlbum[9].Caption;

PhotoAlbum 还提供了一个 RandomSwap 方法,你可以调用该方法将集合中的第一张照片与集合中其他位置随机选择的照片交换:

mPhotoAlbum.RandomSwap ();

由于 的 PhotoAlbum 实现详细信息与理解 RecyclerView无关, PhotoAlbum 因此此处未提供源代码。 的源代码PhotoAlbumRecyclerViewer 示例应用中的 PhotoAlbum.cs 中提供。

布局和初始化

布局文件 Main.axml 由 中的单个RecyclerViewLinearLayout组成:

<?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">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:scrollbars="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

请注意,必须使用完全限定的名称 android.support.v7.widget.RecyclerView ,因为 RecyclerView 已打包在支持库中。 OnCreate的 方法MainActivity初始化此布局,实例化适配器,并准备基础数据源:

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Prepare the data source:
        mPhotoAlbum = new PhotoAlbum ();

        // Instantiate the adapter and pass in its data source:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);

        // Set our view from the "main" layout resource:
        SetContentView (Resource.Layout.Main);

        // Get our RecyclerView layout:
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug the adapter into the RecyclerView:
        mRecyclerView.SetAdapter (mAdapter);

此代码将执行以下操作:

  1. 实例化 PhotoAlbum 数据源。

  2. 将相册数据源传递给适配器的构造函数, PhotoAlbumAdapter (本指南) 稍后定义。 请注意,最佳做法是将数据源作为参数传递给适配器的构造函数。

  3. RecyclerView从布局中获取 。

  4. 通过调用 RecyclerViewSetAdapter 方法将适配器插入实例,RecyclerView如上所示。

布局管理器

中的每个RecyclerView项目都由CardView包含照片图像和照片描述文字 (详细信息涵盖在下面的视图持有人部分) 。 预定义 LinearLayoutManager 用于在垂直滚动排列中布局每个 CardView

mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);

此代码驻留在 main 活动的 OnCreate 方法中。 布局管理器的构造函数需要 上下文,因此 MainActivity 使用 this 进行传递,如上所示。

无需使用预定义 LinearLayoutManager的 ,可以插入可并行显示两 CardView 个项目的自定义布局管理器,实现翻页动画效果以遍历照片集合。 在本指南的后面部分,你将看到如何通过交换其他布局管理器来修改布局的示例。

视图持有者

视图持有者类称为 PhotoViewHolder。 每个 PhotoViewHolder 实例都保存对 ImageView 关联行项的 和 TextView 的引用,该引用按图示在 中 CardView 布局:

包含 ImageView 和 TextView 的 CardView 示意图

PhotoViewHolder派生自 RecyclerView.ViewHolder ,包含用于存储对 和 TextView 的引用ImageView的属性,如上述布局所示。 PhotoViewHolder 由两个属性和一个构造函数组成:

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

在此代码示例中 PhotoViewHolder ,构造函数 (包装) CardViewPhotoViewHolder 传递对父项视图的引用。 请注意,始终将父项视图转发到基构造函数。 构造PhotoViewHolder函数在父项视图上调用 FindViewById 以查找其每个子视图引用 ImageViewTextView,将结果分别存储在 和 Caption 属性中Image。 适配器稍后使用新数据更新此 CardView子视图时,会从这些属性检索视图引用。

有关 的详细信息 RecyclerView.ViewHolder,请参阅 RecyclerView.ViewHolder 类参考

适配器

适配器为每一行加载 RecyclerView 特定照片的数据。 例如,对于位于行位置 P 的给定照片,适配器将关联数据定位在数据源中的位置 P 处,并将此数据复制到集合中位置 P 处的 RecyclerView 行项。 适配器使用视图支架查找 位于该位置的 和 TextView 的引用ImageView,因此在用户滚动浏览照片集合并重用视图时,它不必重复调用FindViewById这些视图。

RecyclerViewer 中,适配器类派生自 RecyclerView.Adapter 以创建 PhotoAlbumAdapter

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;

    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }
    ...
}

成员 mPhotoAlbum 包含数据源 (传递到构造函数的相册) ;构造函数将相册复制到此成员变量中。 实现以下必需 RecyclerView.Adapter 方法:

  • OnCreateViewHolder – 实例化项目布局文件和视图持有者。

  • OnBindViewHolder – 将指定位置的数据加载到其引用存储在给定视图持有者中的视图中。

  • ItemCount – 返回数据源中的项数。

布局管理器在 将项定位在 内 RecyclerView时调用这些方法。 以下各节将介绍这些方法的实现。

OnCreateViewHolder

当 需要新的视图持有者来表示项时,RecyclerView布局管理器会调用 OnCreateViewHolderOnCreateViewHolder 从视图的布局文件扩充项目视图,并将视图包装在新实例中 PhotoViewHolder 。 构造 PhotoViewHolder 函数查找并存储对布局中的子视图的引用,如前面 在 View Holder 中所述。

每个行项由 CardView 表示,其中包含ImageView照片) (和TextView描述文字) (。 此布局驻留在 PhotoCardView.axml 文件中:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardElevation="4dp"
        card_view:cardUseCompatPadding="true"
        card_view:cardCornerRadius="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="8dp">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/imageView"
                android:scaleType="centerCrop" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="#333333"
                android:text="Caption"
                android:id="@+id/textView"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="4dp" />
        </LinearLayout>
    </android.support.v7.widget.CardView>
</FrameLayout>

此布局表示 中的 RecyclerView单个行项。 下面 OnBindViewHolder 所述的方法 () 将数据从数据源复制到 ImageView 此布局的 和 TextView 中。 OnCreateViewHolder为 中RecyclerView给定的照片位置扩充此布局,并实例化一个新的PhotoViewHolder实例 (,该实例在关联的CardView布局) 中查找和缓存对 ImageViewTextView 子视图的引用:

public override RecyclerView.ViewHolder
    OnCreateViewHolder (ViewGroup parent, int viewType)
{
    // Inflate the CardView for the photo:
    View itemView = LayoutInflater.From (parent.Context).
                Inflate (Resource.Layout.PhotoCardView, parent, false);

    // Create a ViewHolder to hold view references inside the CardView:
    PhotoViewHolder vh = new PhotoViewHolder (itemView);
    return vh;
}

生成的视图持有者实例 vh将返回到布局管理器) (调用方。

OnBindViewHolder

当布局管理器准备好在 RecyclerView的可见屏幕区域中显示特定视图时,它会调用适配器的 OnBindViewHolder 方法,以使用数据源中的内容填充指定行位置的项。 OnBindViewHolder获取指定行位置 (照片的图像资源和照片描述文字) 字符串的照片信息,并将此数据复制到关联的视图中。 视图通过存储在视图持有者对象 (中的引用来定位,该引用通过 holder 参数) 传入:

public override void
    OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
{
    PhotoViewHolder vh = holder as PhotoViewHolder;

    // Load the photo image resource from the photo album:
    vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);

    // Load the photo caption from the photo album:
    vh.Caption.Text = mPhotoAlbum[position].Caption;
}

在这种情况下,必须先将传入的视图持有者对象强制转换为派生的视图持有者类型 (, PhotoViewHolder) 才能使用它。 适配器将图像资源加载到视图持有者属性引用的Image视图中,并将描述文字文本复制到视图持有者属性引用的Caption视图中。 这将关联视图与其数据 绑定

请注意, OnBindViewHolder 这是直接处理数据结构的代码。 在这种情况下, OnBindViewHolder 了解如何将 RecyclerView 项位置映射到数据源中的关联数据项。 在这种情况下,映射很简单,因为位置可用作相册中的数组索引;但是,更复杂的数据源可能需要额外的代码来建立此类映射。

ItemCount

方法 ItemCount 返回数据收集中的项数。 在示例照片查看器应用中,项目计数是相册中的照片数:

public override int ItemCount
{
    get { return mPhotoAlbum.NumPhotos; }
}

有关 的详细信息 RecyclerView.Adapter,请参阅 RecyclerView.Adapter 类参考

将所有内容放在一起

示例照片应用的结果 RecyclerView 实现由创建数据源、布局管理器和适配器的代码组成 MainActivityMainActivitymRecyclerView创建 实例,实例化数据源和适配器,并插入布局管理器和适配器:

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        mPhotoAlbum = new PhotoAlbum();
        SetContentView (Resource.Layout.Main);
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug in the linear layout manager:
        mLayoutManager = new LinearLayoutManager (this);
        mRecyclerView.SetLayoutManager (mLayoutManager);

        // Plug in my adapter:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
        mRecyclerView.SetAdapter (mAdapter);
    }
}

PhotoViewHolder 查找并缓存视图引用:

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

PhotoAlbumAdapter 实现三个必需的方法替代:

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;
    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }

    public override RecyclerView.ViewHolder
        OnCreateViewHolder (ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.From (parent.Context).
                    Inflate (Resource.Layout.PhotoCardView, parent, false);
        PhotoViewHolder vh = new PhotoViewHolder (itemView);
        return vh;
    }

    public override void
        OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
    {
        PhotoViewHolder vh = holder as PhotoViewHolder;
        vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);
        vh.Caption.Text = mPhotoAlbum[position].Caption;
    }

    public override int ItemCount
    {
        get { return mPhotoAlbum.NumPhotos; }
    }
}

编译并运行此代码后,它会创建基本照片查看应用,如以下屏幕截图所示:

包含垂直滚动照片卡的照片查看应用的两个屏幕截图

如果未按上述屏幕截图) 所示 (绘制阴影,请编辑 Properties/AndroidManifest.xml 并将以下属性设置添加到 <application> 元素:

android:hardwareAccelerated="true"

此基本应用仅支持浏览相册。 它不响应项目触摸事件,也不处理基础数据中的更改。 扩展 RecyclerView 示例中添加了此功能。

更改 LayoutManager

RecyclerView由于 的灵活性,可以轻松地修改应用以使用不同的布局管理器。 在以下示例中,修改了此图以使用水平滚动的网格布局而不是垂直线性布局显示相册。 为此,布局管理器实例化已修改为使用 , GridLayoutManager 如下所示:

mLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Horizontal, false);

此代码更改将垂直 LinearLayoutManager 替换为一个 GridLayoutManager ,该网格由两行组成,沿水平方向滚动。 再次编译并运行应用时,你将看到照片显示在网格中,滚动是水平而不是垂直的:

网格中水平滚动照片的应用示例屏幕截图

通过仅更改一行代码,可以修改照片查看应用以使用具有不同行为的不同布局。 请注意,不需要修改适配器代码和布局 XML 来更改布局样式。

在下一主题 扩展 RecyclerView 示例中,此基本示例应用进行了扩展,以处理项目单击事件并在基础数据源更改时更新 RecyclerView