Пример Basic RecyclerView
Чтобы понять, как RecyclerView
работает в обычном приложении, в этом разделе рассматривается простой пример кода, который используется RecyclerView
для отображения большой коллекции фотографий:
RecyclerViewer использует CardView для реализации каждого элемента фотографии в макете RecyclerView
. RecyclerView
Из-за преимуществ производительности этот пример приложения может быстро прокручивать большую коллекцию фотографий гладко и без заметных задержек.
Пример источника данных
В этом примере приложения источник данных "фотоальбом" (представленный классом PhotoAlbum
) предоставляет RecyclerView
содержимое элемента.
PhotoAlbum
— это коллекция фотографий с подписями; при создании экземпляра вы получите готовую коллекцию из 32 фотографий:
PhotoAlbum mPhotoAlbum = new PhotoAlbum ();
Каждый экземпляр фотографии в PhotoAlbum
предоставляет свойства, позволяющие считывать идентификатор ресурса изображения, PhotoID
а также строку Caption
заголовка. Коллекция фотографий организована таким образом, чтобы доступ к каждой фотографии можно получить индексатором. Например, следующие строки кода обращаются к идентификатору ресурса изображения и заголовку для десятой фотографии в коллекции:
int imageId = mPhotoAlbum[9].ImageId;
string caption = mPhotoAlbum[9].Caption;
PhotoAlbum
также предоставляет RandomSwap
метод, который можно вызвать, чтобы заменить первую фотографию в коллекции случайным образом выбранной фотографией в другом месте коллекции:
mPhotoAlbum.RandomSwap ();
Поскольку сведения о PhotoAlbum
реализации не относятся к пониманию RecyclerView
, PhotoAlbum
исходный код не представлен здесь.
Макет и инициализация
Файл макета Main.axml состоит из одного RecyclerView
из LinearLayout
следующих элементов:
<?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
макета.Подключает адаптер к
RecyclerView
экземпляруRecyclerView
SetAdapter
, вызвав метод, как показано выше.
Диспетчер макетов
Каждый элемент в списке RecyclerView
состоит из CardView
элемента, содержащего изображение фотографии и подпись фотографии (сведения рассматриваются в разделе "Владелец представления" ниже). Предопределенный используется LinearLayoutManager
для размещения каждой из них CardView
в вертикальном расположении прокрутки:
mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);
Этот код находится в методе основного OnCreate
действия. Конструктор диспетчеру макетов требует контекста, поэтому он MainActivity
передается, как this
показано выше.
Вместо использования предопределенного LinearLayoutManager
можно подключить пользовательский диспетчер макетов, который отображает два CardView
элемента параллельно, реализуя эффект анимации поворота страницы для обхода коллекции фотографий. Далее в этом руководстве вы увидите пример изменения макета, заменив его в другом диспетчере макетов.
Владелец представления
Класс держателя представления называется PhotoViewHolder
. Каждый PhotoViewHolder
экземпляр содержит ссылки на ImageView
связанный элемент строки, TextView
который представлен в CardView
схеме здесь:
PhotoViewHolder
является производным от RecyclerView.ViewHolder
и содержит свойства для хранения ссылок на них ImageView
и TextView
показанных в приведенном выше макете.
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
кода конструктор передает ссылку на представление родительского элемента (т. е CardView
.), которое PhotoViewHolder
выполняет оболочку. Обратите внимание, что представление родительского элемента всегда пересылается базовому конструктору. Конструктор PhotoViewHolder
вызывает представление родительского элемента, чтобы найти каждую из ссылок на дочернее представление и TextView
ImageView
сохранить результаты соответственно Image
Caption
.FindViewById
Позже адаптер получает ссылки на представления из этих свойств, когда он обновляет 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
Диспетчер макетов вызывает OnCreateViewHolder
, когда RecyclerView
требуется новый держатель представления для представления элемента. OnCreateViewHolder
Увеличивает представление элемента из файла макета представления и упаковывает представление в новый PhotoViewHolder
экземпляр. Конструктор PhotoViewHolder
находит и сохраняет ссылки на дочерние представления в макете, как описано ранее в представлении "Владелец представления".
Каждый элемент строки представлен элементом, 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
экземпляре (который находит и кэширует ссылки на ImageView
представления и TextView
дочерние представления в связанном CardView
макете):
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
реализация примера фото-приложения состоит из MainActivity
кода, который создает источник данных, диспетчер макетов и адаптер. MainActivity
mRecyclerView
создает экземпляр, создает экземпляр источника данных и адаптера, а также подключается к диспетчеру макета и адаптеру:
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; }
}
}
При компиляции и запуске этого кода создается базовое приложение для просмотра фотографий, как показано на следующих снимках экрана:
Если тени не рисуются (как показано на снимке экрана выше), измените свойства/AndroidManifest.xml и добавьте в элемент следующий параметр атрибута <application>
:
android:hardwareAccelerated="true"
Это базовое приложение поддерживает только просмотр фотоальбома. Он не отвечает на события касания элементов и не обрабатывает изменения в базовых данных. Эта функция добавлена в расширение примера RecyclerView.
Изменение макетаManager
RecyclerView
Из-за гибкости приложение легко изменить для использования другого диспетчера макетов. В следующем примере он изменяется, чтобы отобразить фотоальбом с макетом сетки, который прокручивается по горизонтали, а не с вертикальным линейным макетом. Для этого создается экземпляр диспетчера макетов, который будет использоваться GridLayoutManager
следующим образом:
mLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Horizontal, false);
Это изменение кода заменяет вертикальную LinearLayoutManager
GridLayoutManager
с сеткой, состоящей из двух строк, которые прокручиваются в горизонтальном направлении. При компиляции и повторном запуске приложения вы увидите, что фотографии отображаются в сетке, а прокрутка является горизонтальной, а не вертикальной:
Изменив только одну строку кода, можно изменить приложение для просмотра фотографий, чтобы использовать другой макет с другим поведением. Обратите внимание, что ни код адаптера, ни XML макета не должны быть изменены для изменения стиля макета.
В следующем разделе расширение примера RecyclerView это базовое пример приложения расширено для обработки событий щелчка элемента и обновления RecyclerView
при изменении базового источника данных.