Как создать вложенный RecyclerView в Android

Опубликовано: 5 Января, 2022

Вложенный RecyclerView - это реализация RecyclerView в RecyclerView. Пример такого макета можно увидеть в различных приложениях, таких как Play store, где внешний (родительский) RecyclerView имеет вертикальную ориентацию, тогда как внутренние (дочерние) RecyclerView имеют горизонтальную ориентацию. Подобный макет разработан здесь.

Подход:
Шаг 1. Добавьте необходимые зависимости
Добавьте следующие зависимости в файл build.gradle (: app). Первая зависимость, соответствующая RecyclerView, является обязательной, а вторая - для CardView, поэтому она не является обязательной в зависимости от требований пользовательского интерфейса. Поскольку здесь CardView используется в дочернем макете RecyclerView, поэтому добавляется зависимость CardView.
Примечание. После добавления зависимостей нажмите « Синхронизировать сейчас».

implementation ‘androidx.recyclerview:recyclerview:1.0.0’
implementation ‘androidx.cardview:cardview:1.0.0’

Примечание. Для правильных версий зависимостей проверьте здесь.
Шаг 2. Реализуйте родительский RecyclerView
Родительский RecyclerView реализован в файле activity_main. Здесь файл activity_main содержит только родительский RecyclerView, но его можно настроить в соответствии с требованиями.

activity_main

<? xml version = "1.0" encoding = "utf-8" ?>
< androidx.constraintlayout.widget.ConstraintLayout
android:layout_width = "match_parent"
android:layout_height = "match_parent"
tools:context = ".MainActivity" >
<!-- This is the parent RecyclerView-->
< androidx.recyclerview.widget.RecyclerView
android:id = "@+id/parent_recyclerview"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
app:layout_constraintBottom_toBottomOf = "parent"
app:layout_constraintEnd_toEndOf = "parent"
app:layout_constraintHorizontal_bias = "0.498"
app:layout_constraintStart_toStartOf = "parent"
app:layout_constraintTop_toTopOf = "parent"
app:layout_constraintVertical_bias = "0.271" />
</ androidx.constraintlayout.widget.ConstraintLayout >

Шаг 3. Создайте макет элемента для родительского RecyclerView
Определите требования для родительского макета и добавьте соответствующие им представления в XML-файле макета. Здесь файлы макета называются parent_item . В дополнение к дочернему RecyclerView включен TextView для заголовка.

parent_item

<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = " http://schemas.android.com/apk/res/android "
android:orientation = "vertical"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" >
<!--Title-->
< TextView
android:id = "@+id/parent_item_title"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:padding = "12sp"
android:textSize = "18sp"
/>
< LinearLayout
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_margin = "12dp"
android:orientation = "horizontal" >
<!--Child RecyclerView-->
< androidx.recyclerview.widget.RecyclerView
android:id = "@+id/child_recyclerview"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" />
</ LinearLayout >
</ LinearLayout >

Шаг 4. Создайте макет элемента для дочернего RecyclerView
Макет элементов дочернего RecyclerView создается в XML-файле макета с именем child_item . Дочерний RecyclerView включает ImageView и TextView, оба заключены в CardView.
Следующее изображение используется в качестве ресурса ImageView, таким образом , он будет добавлен в папку растяжимой Resouce.

значок

child_item

<? xml version = "1.0" encoding = "utf-8" ?>
< FrameLayout
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" >
<!--CardView that holds the elements
of the child RecyclerView -->
< androidx.cardview.widget.CardView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_margin = "12dp" >
< RelativeLayout
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" >
<!--Image in the CardView-->
<!--Here we have already given
the source for the ImageView
and we will not assign it
in the Java code-->
<!--So all the ImageViews will
hold the same image-->
< ImageView
android:id = "@+id/img_child_item"
android:layout_width = "100dp"
android:layout_height = "100dp"
android:layout_marginStart = "5dp"
android:background = "@color/colorPrimaryDark"
android:src = "@drawable/icon" />
< LinearLayout
android:layout_width = "wrap_content"
android:layout_height = "60dp"
android:layout_toEndOf = "@id/img_child_item"
android:padding = "12dp"
android:layout_below = "@+id/img_child_item"
android:layout_alignParentStart = "true"
android:orientation = "vertical" >
<!--Text in the CardView-->
< TextView
android:id = "@+id/child_item_title"
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:textStyle = "bold"
/>
</ LinearLayout >
</ RelativeLayout >
</ androidx.cardview.widget.CardView >
</ FrameLayout >

Шаг 5: Создайте классы элементов для каждого из RecyclerViews.
Для каждого RecyclerView требуется класс Java, в котором конструктор инициализирует параметры элемента и объявляются методы получения и установки. Таким образом, создайте следующие классы Java:

  • ChildItem: этот класс будет реализовывать методы конструктора, получателя и установщика для представлений макета child_item, которые мы хотим установить динамически.
  • ParentItem: этот класс будет реализовывать методы конструктора, получателя и установщика для представлений макета parent_item, которые мы хотим установить динамически.

ChildItem

package com.example.nestedrecyclerview;
public class ChildItem {
// Declaration of the variable
private String ChildItemTitle;
// Constructor of the class
// to initialize the variable*
public ChildItem(String childItemTitle)
{
this .ChildItemTitle = childItemTitle;
}
// Getter and Setter method
// for the parameter
public String getChildItemTitle()
{
return ChildItemTitle;
}
public void setChildItemTitle(
String childItemTitle)
{
ChildItemTitle = childItemTitle;
}
}

ParentItem

package com.example.nestedrecyclerview;
import java.util.List;
public class ParentItem {
// Declaration of the variables
private String ParentItemTitle;
private List<ChildItem> ChildItemList;
// Constructor of the class
// to initialize the variables
public ParentItem(
String ParentItemTitle,
List<ChildItem> ChildItemList)
{
this .ParentItemTitle = ParentItemTitle;
this .ChildItemList = ChildItemList;
}
// Getter and Setter methods
// for each parameter
public String getParentItemTitle()
{
return ParentItemTitle;
}
public void setParentItemTitle(
String parentItemTitle)
{
ParentItemTitle = parentItemTitle;
}
public List<ChildItem> getChildItemList()
{
return ChildItemList;
}
public void setChildItemList(
List<ChildItem> childItemList)
{
ChildItemList = childItemList;
}
}

Шаг 6: Создайте класс адаптера для каждого из RecyclerViews.
Класс адаптера требуется для передачи данных, которые должны отображаться в представлениях RecyclerView.
Класс адаптера необходим как для классов ChildItem, так и для классов ParentItem.

  • ChildAdapter: это класс адаптера для хранения информации, которая должна отображаться в представлениях класса ChildItem.
  • ParentAdapter: это класс адаптера для хранения информации, которая должна отображаться в представлениях класса ParentItem.

В классе ParentItemAdapter создается экземпляр класса

RecyclerView.RecycledViewPool

Этот класс используется для передачи представлений между родительским RecyclerView и дочерним RecyclerView.

ChildItemAdapter

package com.example.nestedrecyclerview;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class ChildItemAdapter
extends RecyclerView
.Adapter<ChildItemAdapter.ChildViewHolder> {
private List<ChildItem> ChildItemList;
// Constuctor
ChildItemAdapter(List<ChildItem> childItemList)
{
this .ChildItemList = childItemList;
}
@NonNull
@Override
public ChildViewHolder onCreateViewHolder(
@NonNull ViewGroup viewGroup,
int i)
{
// Here we inflate the corresponding
// layout of the child item
View view = LayoutInflater
.from(viewGroup.getContext())
.inflate(
R.layout.child_item,
viewGroup, false );
return new ChildViewHolder(view);
}
@Override
public void onBindViewHolder(
@NonNull ChildViewHolder childViewHolder,
position) int
{
// Create an instance of the ChildItem
// class for the given position
ChildItem childItem
= ChildItemList.get(position);
// For the created instance, set title.
// No need to set the image for
// the ImageViews because we have
// provided the source for the images
// in the layout file itself
childViewHolder
.ChildItemTitle
.setText(childItem.getChildItemTitle());
}
@Override
public int getItemCount()
{
// This method returns the number
// of items we have added
// in the ChildItemList
// ie the number of instances
// of the ChildItemList
// that have been created
return ChildItemList.size();
}
// This class is to initialize
// the Views present
// in the child RecyclerView
class ChildViewHolder
extends RecyclerView.ViewHolder {
TextView ChildItemTitle;
ChildViewHolder(View itemView)
{
super (itemView);
ChildItemTitle
= itemView.findViewById(
R.id.child_item_title);
}
}
}

ParentItemAdapter

package com.example.nestedrecyclerview;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class ParentItemAdapter
extends RecyclerView
.Adapter<ParentItemAdapter.ParentViewHolder> {
// An object of RecyclerView.RecycledViewPool
// is created to share the Views
// between the child and
// the parent RecyclerViews
private RecyclerView.RecycledViewPool
viewPool
= new RecyclerView
.RecycledViewPool();
private List<ParentItem> itemList;
ParentItemAdapter(List<ParentItem> itemList)
{
this .itemList = itemList;
}
@NonNull
@Override
public ParentViewHolder onCreateViewHolder(
@NonNull ViewGroup viewGroup,
int i)
{
// Here we inflate the corresponding
// layout of the parent item
View view = LayoutInflater
.from(viewGroup.getContext())
.inflate(
R.layout.parent_item,
viewGroup, false );
return new ParentViewHolder(view);
}
@Override
public void onBindViewHolder(
@NonNull ParentViewHolder parentViewHolder,
position) int
{
// Create an instance of the ParentItem
// class for the given position
ParentItem parentItem
= itemList.get(position);
// For the created instance,
// get the title and set it
// as the text for the TextView
parentViewHolder
.ParentItemTitle
.setText(parentItem.getParentItemTitle());
// Create a layout manager
// to assign a layout
// to the RecyclerView.
// Here we have assigned the layout
// as LinearLayout with vertical orientation
LinearLayoutManager layoutManager
= new LinearLayoutManager(
parentViewHolder
.ChildRecyclerView
.getContext(),
LinearLayoutManager.HORIZONTAL,
false );
// Since this is a nested layout, so
// to define how many child items
// should be prefetched when the
// child RecyclerView is nested
// inside the parent RecyclerView,
// we use the following method
layoutManager
.setInitialPrefetchItemCount(
parentItem
.getChildItemList()
.size());
// Create an instance of the child
// item view adapter and set its
// adapter, layout manager and RecyclerViewPool
ChildItemAdapter childItemAdapter
= new ChildItemAdapter(
parentItem
.getChildItemList());
parentViewHolder
.ChildRecyclerView
.setLayoutManager(layoutManager);
parentViewHolder