В прошлой статье были описаны общие моменты применения компонентов-пагинаторов VerticalPager и HorizontalPager. В этой статье рассмотрим примитивный пример применения постраничного вывода. В частности, определим следующее приложение:
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.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlin.random.Random class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent @OptIn(ExperimentalFoundationApi::class){ // данные для отображения val data = listOf("iPhone 15 Pro", "Redmi Note 12 Pro+", "Galaxy S23 Ultra", "Infinix NOTE 30 Pro", "Honor 90") // состояние val pagerState = rememberPagerState { data.size } HorizontalPager(state = pagerState, Modifier.fillMaxHeight()) { page -> Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { val product = data[page] Text(product, fontSize = 43.sp) Box(Modifier .fillMaxWidth(0.9f) .fillMaxHeight(0.7f) .padding(top=40.dp) .background( Color( Random.nextInt(255), Random.nextInt(255), Random.nextInt(255), 255 ) ) ) Box(Modifier .fillMaxWidth(0.9f) .padding(top=20.dp)){ Text("Описание товара $product", fontSize = 28.sp)} } } } } }
Здесь данные для отображения на страницах определены в виде списка data - списка мобильных устройств:
val data = listOf("iPhone 15 Pro", "Redmi Note 12 Pro+", "Galaxy S23 Ultra", "Infinix NOTE 30 Pro", "Honor 90")
Для каждого из элементов списка будет создаваться своя страница.
Создаем состояние PagerState, которое инициализируется количеством элементов списка (фактически количеством страниц):
val pagerState = rememberPagerState { data.size }
Далее определяем горизонтальный пагинатор HorizontalPager:
HorizontalPager(state = pagerState, Modifier.fillMaxHeight()) { page ->
Каждую страницу в пагинаторе фактически будет представлять столбец Column:
Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { val product = data[page] Text(product, fontSize = 43.sp) Box(Modifier .fillMaxWidth(0.9f) .fillMaxHeight(0.7f) .padding(top=40.dp) .background( Color(Random.nextInt(255),Random.nextInt(255),Random.nextInt(255),255) ) ) Box(Modifier.fillMaxWidth(0.9f).padding(top=20.dp)){ Text("Описание товара $product", fontSize = 28.sp) } }
Страница может представлять любой компонент, содержать различные компоненты в зависимости от задачи приложения. Но в данном случае функционально страница разбивается на три части. В начале идет компонент Text с заголовком товара. Потом идет компонент Box, который принимает случайный цвет (здесь можно было бы вставить картинку, но, я думаю, для демонстрации сойдет и обычный Box). И под ним расположено описание товара в виде компонента Box со вложенным компонентом Text.
Таким образом, у нас получится следующий интерфейс страницы:
В итоге при запуске приложения мы сможем переходить к другим страницам с помощью прокручивания по горизонтали:
Хотя здесь применяется горизонтальный пагинатор HorizontalPager, но аналогично можно было бы использовать и вертикальный пагинатор VerticalPager.
Нередко для перемещения по страницам в приложениях применяются специальные кнопки навигации. Посмотрим, как их реализовать. Для этого определим следующее приложение:
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.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowLeft import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.launch import kotlin.random.Random class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent @OptIn(ExperimentalFoundationApi::class){ val data = listOf("iPhone 15 Pro", "Redmi Note 12 Pro+", "Galaxy S23 Ultra", "Infinix NOTE 30 Pro", "Honor 90") val pagerState = rememberPagerState { data.size } val coroutineScope = rememberCoroutineScope() HorizontalPager(state = pagerState, Modifier.fillMaxHeight()) { page -> Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { val product = data[page] Text(product, fontSize = 43.sp) Box(Modifier .fillMaxWidth(0.9f) .fillMaxHeight(0.7f) .padding(top=40.dp) .background( Color(Random.nextInt(255),Random.nextInt(255),Random.nextInt(255),255) ) ) Box(Modifier.fillMaxWidth(0.9f).padding(top=20.dp)){ Text("Описание товара $product", fontSize = 28.sp) } Row { Icon( imageVector = Icons.Default.KeyboardArrowLeft, contentDescription = "Next Page", modifier = Modifier.size(75.dp).clickable { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage - 1) } } ) Icon( imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "Next Page", modifier = Modifier.size(75.dp).clickable { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } } ) } } } } } }
Для определения кнопок навигации здесь определена строка Row, которая содержит пару иконок Icon:
Для каждой иконки определен обработчик нажатия. Для выполнения перехода между страницами применяется метод pagerState.animateScrollToPage()
, который переходит к странице с определенным индексом. Но поскольку
этот метод представляет suspend-функцию, то он запускается из области корутины
Icon( .......................... modifier = Modifier.size(75.dp).clickable { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage - 1) } } )
Для перехода по страницам странице с помощью состояния пагинатора получаем текущую страницу - pagerState.currentPage
. Соответственно для получения индекса следующей страницы нам надо прибаить 1, а для
получения индекса предыдущей страницы - отнять 1 от номера текущей страницы.