Создавайте динамические списки с помощью RecyclerView (часть Android Jetpack) .

Попробуйте способ создания композиций.
Jetpack Compose — это рекомендуемый набор инструментов для создания пользовательского интерфейса для Android. Узнайте, как работать с макетами в Compose.

RecyclerView позволяет легко и эффективно отображать большие наборы данных. Вы предоставляете данные и определяете внешний вид каждого элемента, а библиотека RecyclerView динамически создает элементы по мере необходимости.

Как следует из названия, RecyclerView повторно использует отдельные элементы. Когда элемент прокручивается за пределы экрана, RecyclerView не уничтожает его представление. Вместо этого RecyclerView повторно использует представление для новых элементов, которые прокрутились на экране. RecyclerView повышает производительность и отзывчивость вашего приложения, а также снижает энергопотребление.

Ключевые классы

Для создания динамического списка используются несколько классов, работающих совместно.

  • RecyclerView — это ViewGroup , содержащий представления, соответствующие вашим данным. Это само по себе представление, поэтому вы добавляете RecyclerView в свой макет так же, как и любой другой элемент пользовательского интерфейса.

  • Каждый отдельный элемент в списке определяется объектом- держателем представления . При создании держателя представления с ним не связаны никакие данные. После создания держателя представления RecyclerView связывает его с данными. Держатель представления определяется путем наследования класса RecyclerView.ViewHolder .

  • RecyclerView запрашивает представления и связывает их с данными, вызывая методы в адаптере. Адаптер определяется путем наследования RecyclerView.Adapter .

  • Менеджер компоновки упорядочивает отдельные элементы в вашем списке. Вы можете использовать один из менеджеров компоновки, предоставляемых библиотекой RecyclerView, или определить свой собственный. Все менеджеры компоновки основаны на абстрактном классе LayoutManager из этой библиотеки.

Вы можете увидеть, как все части взаимодействуют друг с другом, в примере приложения RecyclerView (Kotlin) или примере приложения RecyclerView (Java) .

Шаги по реализации RecyclerView

Если вы собираетесь использовать RecyclerView, вам нужно будет выполнить несколько действий. Они подробно описаны в следующих разделах.

  1. Определите внешний вид списка или сетки. Обычно для этого можно использовать один из стандартных менеджеров компоновки библиотеки RecyclerView.

  2. Разработайте внешний вид и поведение каждого элемента списка. На основе этого дизайна расширьте класс ViewHolder . Ваша версия ViewHolder предоставляет всю функциональность для элементов списка. Ваш ViewHolder — это обертка вокруг View , а управление этим View осуществляется с помощью RecyclerView .

  3. Определите Adapter , который связывает ваши данные с представлениями ViewHolder .

Также доступны расширенные параметры настройки , позволяющие адаптировать RecyclerView под ваши конкретные потребности.

Спланируйте свою планировку.

Элементы в вашем RecyclerView упорядочиваются с помощью класса LayoutManager . Библиотека RecyclerView предоставляет три менеджера компоновки, которые обрабатывают наиболее распространенные ситуации компоновки:

  • LinearLayoutManager располагает элементы в одномерном списке.
  • GridLayoutManager располагает элементы в двумерной сетке:
    • Если сетка расположена вертикально, GridLayoutManager пытается сделать так, чтобы все элементы в каждой строке имели одинаковую ширину и высоту, но разные строки могут иметь разную высоту.
    • Если сетка расположена горизонтально, GridLayoutManager пытается сделать так, чтобы все элементы в каждом столбце имели одинаковую ширину и высоту, но разные столбцы могут иметь разную ширину.
  • StaggeredGridLayoutManager похож на GridLayoutManager , но не требует, чтобы элементы в строке имели одинаковую высоту (для вертикальных сеток) или элементы в столбце имели одинаковую ширину (для горизонтальных сеток). В результате элементы в строке или столбце могут оказаться смещенными относительно друг друга.

Вам также необходимо разработать расположение отдельных элементов. Это расположение понадобится вам при проектировании элемента представления, как описано в следующем разделе.

Установите адаптер и держатель изображения.

После определения макета необходимо реализовать Adapter и ViewHolder . Эти два класса работают вместе, определяя способ отображения данных. ViewHolder — это обертка вокруг View , содержащая макет для отдельного элемента списка. Adapter создает объекты ViewHolder по мере необходимости, а также устанавливает данные для этих представлений. Процесс связывания представлений с их данными называется привязкой (binding).

При определении адаптера вы переопределяете три ключевых метода:

  • onCreateViewHolder() : RecyclerView вызывает этот метод всякий раз, когда ему нужно создать новый ViewHolder . Метод создает и инициализирует ViewHolder и связанный с ним View , но не заполняет содержимое View — ViewHolder еще не привязан к конкретным данным.

  • onBindViewHolder() : RecyclerView вызывает этот метод для связывания ViewHolder с данными. Метод получает необходимые данные и использует их для заполнения макета ViewHolder. Например, если RecyclerView отображает список имен, метод может найти подходящее имя в списке и заполнить виджет TextView в ViewHolder.

  • Метод getItemCount() RecyclerView используется для получения размера набора данных. Например, в приложении адресной книги это может быть общее количество адресов. RecyclerView использует это значение, чтобы определить, когда больше нет элементов, которые можно отобразить.

Вот типичный пример простого адаптера с вложенным ViewHolder , отображающим список данных. В данном случае RecyclerView отображает простой список текстовых элементов. Адаптеру передается массив строк, содержащих текст элементов ViewHolder .

Котлин

class CustomAdapter(private val dataSet: Array<String>) :
        RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView

        init {
            // Define click listener for the ViewHolder's View
            textView = view.findViewById(R.id.textView)
        }
    }

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        // Create a new view, which defines the UI of the list item
        val view = LayoutInflater.from(viewGroup.context)
                .inflate(R.layout.text_row_item, viewGroup, false)

        return ViewHolder(view)
    }

    // Replace the contents of a view (invoked by the layout manager)
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.textView.text = dataSet[position]
    }

    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = dataSet.size

}

Java

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {

    private String[] localDataSet;

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public ViewHolder(View view) {
            super(view);
            // Define click listener for the ViewHolder's View

            textView = (TextView) view.findViewById(R.id.textView);
        }

        public TextView getTextView() {
            return textView;
        }
    }

    /**
     * Initialize the dataset of the Adapter
     *
     * @param dataSet String[] containing the data to populate views to be used
     * by RecyclerView
     */
    public CustomAdapter(String[] dataSet) {
        localDataSet = dataSet;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view, which defines the UI of the list item
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.text_row_item, viewGroup, false);

        return new ViewHolder(view);
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.getTextView().setText(localDataSet[position]);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return localDataSet.length;
    }
}

Как обычно, макет для каждого элемента представления определяется в XML-файле макета. В данном случае приложение имеет файл text_row_item.xml следующего вида:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_height"
    android:layout_marginLeft="@dimen/margin_medium"
    android:layout_marginRight="@dimen/margin_medium"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/element_text"/>
</FrameLayout>

Следующие шаги

Следующий фрагмент кода демонстрирует, как можно использовать RecyclerView .

Котлин

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dataset = arrayOf("January", "February", "March")
        val customAdapter = CustomAdapter(dataset)

        val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = customAdapter

    }

}

Java

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.layoutManager = new LinearLayoutManager(this)
recyclerView.setAdapter(customAdapter);

Библиотека также предлагает множество способов настройки вашей реализации. Для получения дополнительной информации см. раздел «Расширенная настройка RecyclerView» .

Включить дисплей от края до края

Выполните следующие действия, чтобы включить отображение RecyclerView от края до края:

  • Для настройки обратно совместимого дисплея с безрамочным экраном вызовите функцию enableEdgeToEdge() .
  • Если элементы списка изначально перекрывают системные панели, примените отступы к RecyclerView . Это можно сделать, установив android:fitsSystemWindows в true или используя ViewCompat.setOnApplyWindowInsetsListener .
  • Чтобы элементы списка отображались под системными панелями во время прокрутки, установите для свойства android:clipToPadding в значение false для элемента RecyclerView .

На следующем видео показан RecyclerView с отключенным (слева) и включенным (справа) отображением от края до края:

Пример вставного кода:

Котлин

ViewCompat.setOnApplyWindowInsetsListener(
  findViewById(R.id.my_recycler_view)
  ) { v, insets ->
      val innerPadding = insets.getInsets(
          WindowInsetsCompat.Type.systemBars()
                  or WindowInsetsCompat.Type.displayCutout()
          // If using EditText, also add
          // "or WindowInsetsCompat.Type.ime()" to
          // maintain focus when opening the IME
      )
      v.setPadding(
          innerPadding.left,
          innerPadding.top,
          innerPadding.right,
          innerPadding.bottom)
      insets
  }
  

Java

ViewCompat.setOnApplyWindowInsetsListener(
  activity.findViewById(R.id.my_recycler_view),
  (v, insets) -> {
      Insets innerPadding = insets.getInsets(
              WindowInsetsCompat.Type.systemBars() |
                      WindowInsetsCompat.Type.displayCutout()
              // If using EditText, also add
              // "| WindowInsetsCompat.Type.ime()" to
              // maintain focus when opening the IME
      );
      v.setPadding(
              innerPadding.left,
              innerPadding.top,
              innerPadding.right,
              innerPadding.bottom
      );
      return insets;
  }
);
  

XML-код RecyclerView :

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:clipToPadding="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Дополнительные ресурсы

Для получения дополнительной информации о тестировании на Android обратитесь к следующим ресурсам.

Примеры приложений