LazyColumn и LazyRow позволяют использовать прикрепленные заголовки или sticky headers. То есть мы можем сгруппировать элементы списка под соответствующим заголовком. И припрепленные заголовки остаются видимыми на экране во время прокрутки текущей группы. Как только группа прокручивается из поля зрения, ее место занимает заголовок следующей группы.
Для создания прикрепленных заголовков применяется функция LazyListScope.stickyHeader(). А содержимое списка должно храниться в массиве или списке,
который трансформируется в группы с помощью функции Kotlin groupBy(). Функция groupBy()
принимает лямбду-селектор,
которая определяет, как данные должны быть сгруппированы. Этот селектор затем служит ключом для доступа к элементам каждой группы.
Рассмотрим, например, следующий список, содержащий модели смартфонов:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Text import androidx.compose.ui.unit.sp import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent @OptIn(ExperimentalFoundationApi::class){ // начальные данные val phones = listOf("Apple iPhone 15 Pro", "Realme 11 PRO", "Google Pixel 5", "Samsung Galaxy S24 Ultra", "Google Pixel 6", "Samsung Galaxy S21 FE", "Apple iPhone 15 Pro Max", "Xioami Redmi Note 12", "Xiaomi Redmi 12", "Apple iPhone 13", "Google Pixel 6", "Apple iPhone 14", "Realme C30s", "Realme Note 50") // создаем группы val groups = phones.groupBy { it.substringBefore(" ") } LazyColumn( contentPadding = PaddingValues(5.dp) ){ groups.forEach { (brand, models) -> stickyHeader { Text( text = brand, fontSize = 28.sp, color = Color.White, modifier = Modifier.background(Color.Gray).padding(5.dp).fillMaxWidth() ) } items(models) { model -> Text(model, Modifier.padding(5.dp), fontSize = 28.sp) } } } } } }
Прежде всего стоит отметить, что на момент написания текущей статьи функциональность LazyListScope.stickyHeader() является экспериментальной,
поэтому компонент, который использует данную функцию, надо предварять аннотацией @OptIn(ExperimentalFoundationApi::class)
setContent @OptIn(ExperimentalFoundationApi::class){ ..... }
Сами выводимые данные представляют список phones, который содержит названия моделей смартфонов. С помощью функцию groupBy()
группируем элементы этого списка по начальной подстроке:
val groups = phones.groupBy { it.substringBefore(" ") }
Здесь it
- это каждая строка из списка, соответственно выражение it.substringBefore(" ")
получает в каждой строке списка подстроку, которая идет до пробела. Например, из строки
"Apple iPhone 15 Pro" получаем подстроку "Apple". В данном случае мы условимся, что эта подстрока будет представлять производителя смартфона. И функция groupBy группирует все данные по полученным подстрокам-производителям. В итоге получается словарь типа
Map<String, List<String>>
, где ключами являются подстрокам-производители, а значениями - подсписок смартфонов конкретно данного производителя.
Далее перебираем весь этот словарь:
groups.forEach { (brand, models) -> stickyHeader { Text( text = brand, fontSize = 28.sp, color = Color.White, modifier = Modifier.background(Color.Gray).padding(5.dp).fillMaxWidth() ) } items(models) { model -> Text(model, Modifier.padding(5.dp), fontSize = 28.sp) } }
И для каждого элемента словаря для ключа используем функцию stickyHeader()
- в ней создаем компонент Text, который будет представлять собственно заголовок и будет отображать ключ -производителя смартфонов.
И также для каждого элемента словаря применяется функция items()
, которая отображает список смартфонов конкретного данного производителя. В итоге мы получим следующее приложение:
Обратите внимание, что хотя группа Apple частично прокручивается вне поля зрения, заголовок остается в верхней части экрана.
Другой пример:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Text import androidx.compose.ui.unit.sp import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent @OptIn(ExperimentalFoundationApi::class){ // начальные данные val people = listOf( Person("Tom", "Microsoft"), Person("Alice", "Microsoft"), Person("Bob", "Google"), Person("Sam", "JetBrains"), Person("Kate", "Google"), Person("Mark", "Google"), Person("Bill", "Microsoft"), Person("Sandra", "JetBrains"), Person("Lisa", "Apple"), Person("Alex", "Apple") ) // создаем группы val groups = people.groupBy { it.company } LazyColumn( contentPadding = PaddingValues(5.dp) ){ groups.forEach { (company, employees) -> stickyHeader { Text( text = company, fontSize = 28.sp, color = Color.White, modifier = Modifier.background(Color.Gray).padding(5.dp).fillMaxWidth() ) } items(employees) { employee -> Text(employee.name, Modifier.padding(5.dp), fontSize = 28.sp) } } } } } } data class Person(val name:String, val company: String)
Здесь данные представляют сложные данные объекта Person, который определяет два свойства: name и company. Группировка идет по свойству company: