Заметка
У вас должна быть установлена Android Studio Arctic Fox и выше, чтобы использовать Jetpack Compose в вашем проекте.
В данном туториале мы рассмотрим, как создавать вкладки (tabs) с помощью Jetpack Compose и прокручиваться между ними используя Pager из группы библиотек Accompanist.
Добавление библиотек
Зайдите в ваш project-level gradle.build файл и добавьте следующее расширение:
buildscript {
ext {
compose_version = '1.0.0-rc02'
}
// ...
}
Теперь, зайдите в ваш app-level gradle.build и добавьте следующее:
android {
// ...
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
// ...
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
}
dependencies {
// ...
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.navigation:navigation-compose:2.4.0-alpha04"
implementation "androidx.activity:activity-compose:1.3.0-rc02"
// Accompanist
implementation "com.google.accompanist:accompanist-pager:0.14.0" // Pager
implementation "com.google.accompanist:accompanist-pager-indicators:0.14.0" // Pager Indicators
// ...
}
Создание View (Экранов)
Перед тем как, мы начнем работу со вкладками, давайте создадим View (или Экраны), которые мы будем отображать, когда выберем вкладку.
В этом примере я создал Kotlin файл названый ContentScreens, и добавил три разных Экрана, которые показывают имя view в Text в середине экрана.
@Composable
fun MusicScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.colorPrimary))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Music View",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Preview(showBackground = true)
@Composable
fun MusicScreenPreview() {
MusicScreen()
}
@Composable
fun MoviesScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.colorPrimary))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Movies View",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Preview(showBackground = true)
@Composable
fun MoviesScreenPreview() {
MoviesScreen()
}
@Composable
fun BooksScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.colorPrimary))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Books View",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Preview(showBackground = true)
@Composable
fun BooksScreenPreview() {
BooksScreen()
}
Создание элементов вкладки
Дальше, мы создадим новый файл, который содержит всё для каждого элемента вкладки (иконка, название и экран, который мы будем отображать).
Создайте новый изолированный класс (sealed class). Зайдите в папку вашего проекта > Правый клик > New > Kotlin Class/File
В новом окне, выберите изолированный класс и дайте ему имя TabItem.
Чтобы иметь возможность извлекать View (Экран) из списка (вы поймете, о чем я говорю в дальнейшем) как @Composable, а не как Unit тип, мы создаем typealias, который меняет Unit на @Composable
typealias ComposableFun = @Composable () -> Unit
sealed class TabItem(var icon: Int, var title: String, var screen: ComposableFun) {
object Music : TabItem(R.drawable.ic_music, "Music", { MusicScreen() })
object Movies : TabItem(R.drawable.ic_movie, "Movies", { MoviesScreen() })
object Books : TabItem(R.drawable.ic_book, "Books", { BooksScreen() })
}
Создание View (Экрана) для вкладок
Теперь, давайте создадим View (Экран), который будет содержать Вкладки и View.
Создайте новую, составную функцию и дайте ей имя MainScreen.
Внутри этой функции, мы создаем список для вкладок. Переменная, которая сохраняет состояние пейджера. И мы настраиваем Scaffold layout.
class MainActivity : ComponentActivity() {
@ExperimentalPagerApi
@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen()
}
}
}
@ExperimentalPagerApi
@ExperimentalMaterialApi
@Composable
fun MainScreen() {
val tabs = listOf(
TabItem.Music,
TabItem.Movies,
TabItem.Books
)
val pagerState = rememberPagerState(pageCount = tabs.size)
Scaffold(
topBar = { /* Add code later },
) {
/* Add code later */
}
}
@ExperimentalPagerApi
@ExperimentalMaterialApi
@Preview(showBackground = true)
@Composable
fun MainScreenPreview() {
MainScreen()
}
Создание Top Bar
В этом же файле, добавьте следующую составную функцию, чтобы создать Top Bar
@Composable
fun TopBar() {
TopAppBar(
title = { Text(text = stringResource(R.string.app_name), fontSize = 18.sp) },
backgroundColor = colorResource(id = R.color.colorPrimary),
contentColor = Color.White
)
}
@Preview(showBackground = true)
@Composable
fun TopBarPreview() {
TopBar()
}
Создание Вкладок
Создайте новую, составную функцию для вкладок с двумя параметрами. Список элементов вкладок и состояние пейджера.
@ExperimentalPagerApi
@ExperimentalMaterialApi
@Composable
fun Tabs(tabs: List, pagerState: PagerState) {
val scope = rememberCoroutineScope()
// OR ScrollableTabRow()
TabRow(
selectedTabIndex = pagerState.currentPage,
backgroundColor = colorResource(id = R.color.colorPrimaryDark),
contentColor = Color.White,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
)
}) {
tabs.forEachIndexed { index, tab ->
// OR Tab()
LeadingIconTab(
icon = { Icon(painter = painterResource(id = tab.icon), contentDescription = "") },
text = { Text(tab.title) },
selected = pagerState.currentPage == index,
onClick = {
scope.launch {
pagerState.animateScrollToPage(index)
}
},
)
}
}
}
@ExperimentalMaterialApi
@ExperimentalPagerApi
@Preview(showBackground = true)
@Composable
fun TabsPreview() {
val tabs = listOf(
TabItem.Music,
TabItem.Movies,
TabItem.Books
)
val pagerState = rememberPagerState(pageCount = tabs.size)
Tabs(tabs = tabs, pagerState = pagerState)
}
Настройка Views для Вкладок
В том же файле снова, создайте новую, составную функцию TabsContent с теми же двумя параметрами, как до этого, список элементов вкладок и состояние пейджера.
В этой функции, у нас есть HorizontalPager, который берет состояние пейджера. И мы отображаем View (или Экран) вкладки, которая была выбрана.
@ExperimentalPagerApi
@Composable
fun TabsContent(tabs: List, pagerState: PagerState) {
HorizontalPager(state = pagerState) { page ->
tabs[page].screen()
}
}
@ExperimentalMaterialApi
@ExperimentalPagerApi
@Preview(showBackground = true)
@Composable
fun TabsContentPreview() {
val tabs = listOf(
TabItem.Music,
TabItem.Movies,
TabItem.Books
)
val pagerState = rememberPagerState(pageCount = tabs.size)
TabsContent(tabs = tabs, pagerState = pagerState)
}
Соединение TopBar, Вкладок (Tabs) и TabsContent View
Теперь, в последним шаге, настало время соединить все view вместе.
Вернитесь к MainScreen и добавьте TopBar к Scaffold’s layout topBar модификатору и создайте новый Column внутри скобок.
Внутри Column, добавьте вкладки и TabsContent и передайте параметры вкладок и pagerState.
@ExperimentalPagerApi
@ExperimentalMaterialApi
@Composable
fun MainScreen() {
val tabs = listOf(TabItem.Music, TabItem.Movies, TabItem.Books)
val pagerState = rememberPagerState(pageCount = tabs.size)
Scaffold(
topBar = { TopBar() },
) {
Column {
Tabs(tabs = tabs, pagerState = pagerState)
TabsContent(tabs = tabs, pagerState = pagerState)
}
}
}
@ExperimentalPagerApi
@ExperimentalMaterialApi
@Preview(showBackground = true)
@Composable
fun MainScreenPreview() {
MainScreen()
}
Вы можете найти готовый проект здесь.