Прикрепленные заголовки

Последнее обновление: 09.04.2024

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(), которая отображает список смартфонов конкретного данного производителя. В итоге мы получим следующее приложение:

stickyHeader в LazyColumn в мобильном приложении Jetpack Compose на Kotlin на Android

Обратите внимание, что хотя группа 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:

stickyHeader в LazyColumn в Jetpack Compose на Kotlin на Android
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850