为了了解典型应用程序中的工作原理 RecyclerView ,本主题探讨了 RecyclerViewer 示例应用,这是一个简单的代码示例,用于 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 因此此处未显示源代码。 在 RecyclerViewer 示例应用中的 PhotoAlbumPhotoAlbum.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);

此代码将执行以下操作:

  • 实例化 PhotoAlbum 数据源。

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

  • RecyclerView从布局中获取。

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

    布局管理器

    每个 RecyclerView 项目由包含照片图像和照片标题的项 CardView 组成, (详细信息显示在) 下方的 “视图持有者 ”部分。 预定义 LinearLayoutManager 用于在垂直滚动排列中布局每个 CardView 布局:

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

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

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

    视图持有者

    视图持有者类被调用 PhotoViewHolder。 每个 PhotoViewHolder 实例都保留对 ImageView 关联行项和 TextView 关联的行项的引用,该项按 CardView 此处所示布局:

    PhotoViewHolder派生自RecyclerView.ViewHolder并包含用于存储对ImageViewTextView上述布局中显示的引用的属性。 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构造函数 (包装) PhotoViewHolder传递对父项视图CardView的引用。 请注意,始终将父项视图转发到基本构造函数。 构造PhotoViewHolder函数调用FindViewById父项视图来查找其每个子视图引用,ImageViewTextView分别将结果存储在属性中ImageCaption。 适配器稍后使用新数据更新此 CardView子视图时,会从这些属性中检索视图引用。

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

    适配器使用特定照片的数据加载每 RecyclerView 行。 例如,对于行位置 P 处的给定照片,适配器在数据源中的位置 P 中找到关联的数据,并将此数据复制到集合中位置 P 处的 RecyclerView 行项。 适配器使用视图持有者查找该 ImageView 位置的 TextView 引用,以便用户滚动浏览照片集合并重复使用视图时,无需重复调用 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 函数根据 前面在视图持有者中所述的布局中查找和存储对子视图的引用。

    每个行项由 CardViewImageView 包含照片) 的 (和 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实例 (,该实例在关联的布局中查找和缓存对ImageView关联CardView布局中和TextView子视图的引用) :

    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

  • RecyclerViewer (示例)
  • RecyclerView
  • RecyclerView 部件和功能
  • 扩展 RecyclerView 示例
  • RecyclerView
  •