August 9, 2019

Реализация списка с заголовком, футером и пагинацией в Android

RecyclerView

RecyclerView — это расширенная версия ListView с некоторыми улучшениями в производительности и с новыми функциями. Как следует из названия, RecyclerView перерабатывает и повторно использует представления элементов при прокрутке. В RecyclerView гораздо проще добавлять анимации по сравнению с ListView. В этом уроке мы разберем, как создать RecyclerView с заголовком, футером, разбиением на страницы и анимацией.

Настройка Gradle

Добавьте следующую зависимость в файл build.gradle:

 //пожалуйста, проверьте последнюю версию
 compile 'com.android.support:recyclerview-v7:23.1.1'

Добавление RecyclerView в XML представление

После того как проект будет синхронизирован, добавьте компонент RecyclerView в ваш макет:

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycleView"
    android:clipToPadding="false"
    android:padding="8dp"
    android:layout_height="match_parent"
    android:layout_width="match_parent"/>

Привязка XML с Activity

Теперь в методе onCreate вашей активности добавьте следующий код:

override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)

    // setContentView и прочий код

    recyclerView = findViewById(R.id.recycleView)

    // Установите true, если ваш RecyclerView ограничен и имеет фиксированный размер
    recyclerView.setHasFixedSize(false)

    // Установите требуемый LayoutManager
    layoutManager = LinearLayoutManager(this)
    recyclerView.setLayoutManager(layoutManager)

    // Инициализирование и установка адаптера в RecyclerView
    adapter = MyAdapter(list)
    recyclerView.setAdapter(adapter)
}

Прежде чем идти дальше, давайте подробно рассмотрим приведенный выше код:

  • setHasFixedSize — Установите значение true, если вы не изменяете высоту или ширину у RecyclerView.
  • RecyclerView Adapter — RecyclerView требует адаптер заполнения и управления элементами. Здесь мы передаем ArrayList со значениями в Adapter. Подробное описание адаптера в следующем разделе.
  • Layout Manager — Простыми словами, Layout Manager помогает нам определить структуру нашего RecyclerView. Есть три встроенных Layout Managers. Помимо этого, мы можем создать собственный пользовательский Layout Manager, чтобы удовлетворить наши требования.

Виды Layout Manager:

  • LinearLayoutManager показывает элементы в списке с вертикальной или горизонтальной прокруткой.
  • GridLayoutManager показывает элементы в сетке.
  • StaggeredGridLayoutManager показывает элементы в шахматной сетке.

RecyclerView ItemDecoration

ItemDecoration позволяет приложению добавлять специальный полосы и смещения к определенным представлениям элементов из набора данных адаптера. Это может быть полезно для рисования разделителей между элементами, выделениями, границами визуальной группировки и т. д. – developer.android.com

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

class ItemOffsetDecoration(private val offset: Int) : RecyclerView.ItemDecoration() {

    override fun getItemOffsets(outRect: Rect, view: View,
                                parent: RecyclerView, state: RecyclerView.State) {

        // Добавление отступов к нулевому элементу
        if (parent.getChildAdapterPosition(view) == 0) {
            outRect.right = offset;
            outRect.left = offset;
            outRect.top = offset;
            outRect.bottom = offset;
        }
    }
}

В вышеприведенном классе мы устанавливаем отступы к нулевому элементу.

// Внутри onCreate перед установкой адаптера в RecyclerView
// Здесь мы устанавливаем отступ, равный 20
recyclerView.addItemDecoration(ItemOffsetDecoration(20));

RecyclerView Adapter

Теперь давайте настроим адаптер ReсyclerView с заголовком и футером.

class MyAdapter(
    private val list: List<String>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        private const val TYPE_HEADER = 0
        private const val TYPE_ITEM = 1
        private const val TYPE_FOOTER = 2
    }

    override fun getItemViewType(position: Int) = when {
        position == 0 -> TYPE_HEADER
        position > list.size -> TYPE_FOOTER
        else -> TYPE_ITEM
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) = when (viewType) {
        TYPE_ITEM -> {
            val view = LayoutInflater.from(viewGroup.context)
                        .inflate(R.layout.item_layout, viewGroup, false)
            ItemViewHolder(view)
        }
        TYPE_HEADER -> {
            val view = LayoutInflater.from(viewGroup.context)
                        .inflate(R.layout.header_layout, viewGroup, false)
            HeaderViewHolder(view)
        }
        TYPE_FOOTER -> {
            val view = LayoutInflater.from(viewGroup.context)
                        .inflate(R.layout.footer_layout, viewGroup, false)
            FooterViewHolder(view)
        }
        else -> throw RuntimeException("there is no type that matches the type $viewType," +
                    "make sure your using types correctly")
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is HeaderViewHolder -> {
                // Установите значение из списка в соответствующий компонент
                // пользовательского интерфейса, как показано ниже.
                holder.txtName.text = list[position]
            }
            //Аналогично можно связывать другие компоненты пользовательского интерфейса
            is ItemViewHolder -> {
                // Ваш код здесь
            }
            is FooterViewHolder -> {
                // Ваш код здесь
            }
        }
    }

    override fun getItemCount(): Int {
        // Увеличьте на два для размещения заголовка и подвала
        return this.list.size + 2
    }

    // ViewHolders для заголовка, элемента и подвала

    inner class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val txtName = view.findViewById(R.id.txt_name) as TextView
        // Добавьте свои компоненты ui здесь, как показано выше
    }

    inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        // Добавьте компоненты пользовательского интерфейса здесь
    }

    inner class FooterViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        // Добавьте компоненты пользовательского интерфейса здесь
    }
}

Пагинация

Теперь, когда адаптер готов, давайте посмотрим, как добавить пагинацию в список RecyclerView. Это довольно легко сделать и должно быть добавлено в onCreate после установки Adapter to Recycler-View.

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
        if (lastvisibleitemposition == adapter.getItemCount() - 1) {
            if (!loading && !isLastPage) {
                loading = true
                fetchData(++pageCount)
                // Увеличиваем на 1 pageCount при каждой прокрутке
                // для получения данных со следующей страницы
                // Укажите loading = false после загрузки данных
                // Вызовите adapter.notifyDataSetChanged(), чтобы обновить адаптер и макет
            }
        }
    }
})

Всякий раз когда данные изменяются в list, вызывайте функцию ниже, чтобы обновить адаптер RecyclerView и показать новые данные.

adapter.notifyDataSetChanged();

Надеюсь, что этот пост поможет вам получить общее представление о настройке RecyclerView с заголовком, подвалом и пагинацией.

Источник: Реализация списка с заголовком, футером и пагинацией в Андроид