This is my 11th consecutive game for the First-chair Challenge, and Subtitles for the first-chair challenge are likely to be in Pager layouts

There is currently a Chinese manual project for Jetpack Compose, which aims to help developers better understand and master the Compose framework.

This article is translated and written by myself. It has been published in this manual. Welcome to refer to it.

Please follow the wechat official account Jetpack Compose Museum for more information about Compose technology.

An overview of the

The library provides the Pager component for Jetpack Compose, which has similar features if you’ve used ViewPager before.

⚠️ Note that the paging class component is still experimental and the API is subject to change. All apis are annotated with @experimentalPagerAPI

HorizontalPager

One such layout is the HorizontalPager, which places all the children on a horizontal row, allowing the user to slide horizontally between the children.

The simplest usage is as follows:

// Display 10 items
val pagerState = rememberPagerState(pageCount = 10)

HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}
Copy the code

If you want to jump to a particular page, you can choose to use in CoroutineScope pagerState. ScrollToPage (index) or pagerState. AnimateScrollToPage (index) one.

VerticalPager

VerticalPager is very similar to the HorizontalPager in that it places all the children on a vertical column, allowing the user to slide vertically between the children.

// Display 10 items
val pagerState = rememberPagerState(pageCount = 10)

VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}
Copy the code

Create a delay

All pages in HorizontalPager and VerticalPager are formed and laid out lazily according to layout requirements. As the user slides between pages, any pages that are no longer needed are dynamically removed.

Behind the scenes to limit

The API provided by PagerState allows the setting of initOffscreenLimit, which defines that when the number of pages on either side of the previous page exceeds this limit, the page will be removed and re-created as required. This value defaults to 1, but can be increased to accommodate more content.

val pagerState = rememberPagerState(
    pageCount = 10,
    initialOffscreenLimit = 2,
)

HorizontalPager(state = pagerState) { page ->
    // ...
}
Copy the code

Child scroll effect

A common use case is to apply effects to page subitems that are triggered by scrolling.

HorizontalPagerTransitionSample example demonstrates how to do this.

The Pager component’s Content Scope allows the developer to easily retrieve the currentPage and currentPageOffset references. You can use these values to calculate the effect. We provide a calculateCurrentOffsetForPage () extension function to calculate the offset of a particular page.

import com.google.accompanist.pager.calculateCurrentOffsetForPage

HorizontalPager(state = pagerState) { page ->
    Card(
        Modifier
            .graphicsLayer {
                // Calculate the absolute offset for the current page from the
                // scroll position. We use the absolute value which allows us to mirror
                // any effects for both directions
                val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue

                // We animate the scaleX + scaleY, between 85% and 100%
                lerp(
                    start = 0.85 f,
                    stop = 1f,
                    fraction = 1f - pageOffset.coerceIn(0f.1f)
                ).also { scale ->
                    scaleX = scale
                    scaleY = scale
                }

                // We animate the alpha, between 50% and 100%
                alpha = lerp(
                    start = 0.5 f,
                    stop = 1f,
                    fraction = 1f - pageOffset.coerceIn(0f.1f)}) {// Card content}}Copy the code

Page change response

The PagerState.CurrentPage property is updated whenever the selected page changes. You can use the snapshowFlow method to listen for changes through flow:

LaunchedEffect(pagerState) {
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Selected page has changed...}}Copy the code

indicator

We have also released a sibling library called Pager-Indicators that provides some simple indicator combinations for use with HorizontalPager and VerticalPager.

HorizontalPagerWithIndicatorSample and VerticalPagerWithIndicatorSample will show you how to use these indicators.

Integration Tab

A common use case for HorizontalPager is to be used in conjunction with TabRow or ScrollableTabRow.

There is a Modifier provided in the Pager-Indicators library that can be used on TAB indicators like this.

val pagerState = rememberPagerState(pageCount = pages.size)

TabRow(
    // Our selected tab is our current page
    selectedTabIndex = pagerState.currentPage,
    // Override the indicator, using the provided pagerTabIndicatorOffset modifier
    indicator = { tabPositions ->
        TabRowDefaults.Indicator(
            Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
        )
    }
) {
    // Add tabs for all of our pages
    pages.forEachIndexed { index, title ->
        Tab(
            text = { Text(title) },
            selected = pagerState.currentPage == index,
            onClick = { /* TODO */ },
        )
    }
}

HorizontalPager(state = pagerState) { page ->
    // TODO: page content
}
Copy the code

usage

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-pager:<version>"

    // If using indicators, also depend on 
    implementation "com.google.accompanist:accompanist-pager-indicators:<version>"
}
Copy the code

download

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-swiperefresh:<version>"
}
Copy the code

Each version can be found in the snapshot repository and updated with each commit.