Как создавать вкладки при помощи Jetpack Compose

Туториалы

Как создавать вкладки при помощи Jetpack Compose

Заметка

У вас должна быть установлена 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()
}

Вы можете найти готовый проект здесь.

Оригинал статьи.

Комментарии

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: