Compose'da Pager

İçerikler arasında sol, sağ veya yukarı/aşağı gitmek için sırasıyla HorizontalPager ve VerticalPager composable'ları kullanabilirsiniz. Bu composable'lar, görünüm sistemindeki ViewPager ile benzer işlevlere sahiptir. Varsayılan olarak HorizontalPager, ekranın tam genişliğini kaplar, VerticalPager tüm ekranı kaplar ve çağrı cihazları aynı anda yalnızca bir sayfa çevirir. Bu varsayılanların tümü yapılandırılabilir.

HorizontalPager

Yatay olarak sola ve sağa kaydırılan bir sayfa ayırıcı oluşturmak için HorizontalPager işlevini kullanın:

Şekil 1. HorizontalPager demosu

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

VerticalPager

Yukarı ve aşağı kaydırılan bir çağrı cihazı oluşturmak için VerticalPager öğesini kullanın:

Şekil 2. VerticalPager demosu

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

Tembel oluşturma

Hem HorizontalPager hem de VerticalPager dilindeki sayfalar gerektiğinde gecikmeli ve düzenlenmiştir. Kullanıcı sayfalar arasında gezindikçe composable, artık gerekli olmayan sayfaları kaldırır.

Ekran dışında daha fazla sayfa yükle

Varsayılan olarak, çağrı cihazı yalnızca ekranda görünen sayfaları yükler. Ekran dışında daha fazla sayfa yüklemek için beyondBoundsPageCount öğesini sıfırdan büyük bir değere ayarlayın.

Sayfa ayırıcıda bir öğeye gidin

Sayfa ayırıcıda belirli bir sayfaya gitmek için rememberPagerState() ile bir PagerState nesnesi oluşturun ve bunu sayfa cihazına state parametresi olarak iletin. CoroutineScope içinde, bu durumda PagerState#scrollToPage() çağrısı yapabilirsiniz:

val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp)
    )
}

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.scrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

Sayfaya animasyon eklemek istiyorsanız PagerState#animateScrollToPage() işlevini kullanın:

val pagerState = rememberPagerState(pageCount = {
    10
})

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

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.animateScrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

Sayfa durumu değişiklikleri hakkında bildirim alın

PagerState, sayfalarla ilgili bilgi içeren üç özelliğe sahiptir: currentPage, settledPage ve targetPage.

  • currentPage: Tutturma konumuna en yakın sayfa. Varsayılan olarak, tutturma konumu düzenin başındadır.
  • settledPage: Animasyon veya kaydırma çalışmıyorken kullanılan sayfa numarası. Bu, currentPage özelliğinden farklıdır. currentPage özelliği, sayfa tutturma konumuna yeterince yakın olduğunda hemen güncellenir, ancak settledPage tüm animasyonlar bitene kadar aynı kalır.
  • targetPage: Kaydırma hareketi için önerilen durak konumu.

Bu değişkenlerdeki değişiklikleri gözlemlemek ve bunlara tepki vermek için snapshotFlow işlevini kullanabilirsiniz. Örneğin, her sayfa değişikliğinde bir analiz etkinliği göndermek için aşağıdakileri yapabilirsiniz:

val pagerState = rememberPagerState(pageCount = {
    10
})

LaunchedEffect(pagerState) {
    // Collect from the a snapshotFlow reading the currentPage
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Do something with each page change, for example:
        // viewModel.sendPageSelectedEvent(page)
        Log.d("Page change", "Page changed to $page")
    }
}

VerticalPager(
    state = pagerState,
) { page ->
    Text(text = "Page: $page")
}

Sayfa göstergesi ekleyin

Bir sayfaya gösterge eklemek için, sayfa sayısı arasından hangi sayfanın seçildiğine dair bilgi almak üzere PagerState nesnesini kullanın ve özel göstergenizi çizin.

Örneğin, basit bir daire göstergesi isterseniz pagerState.currentPage kullanarak daire sayısını tekrarlayabilir ve sayfanın seçili olup olmadığına bağlı olarak daire rengini değiştirebilirsiniz:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    modifier = Modifier.fillMaxSize()
) { page ->
    // Our page content
    Text(
        text = "Page: $page",
    )
}
Row(
    Modifier
        .wrapContentHeight()
        .fillMaxWidth()
        .align(Alignment.BottomCenter)
        .padding(bottom = 8.dp),
    horizontalArrangement = Arrangement.Center
) {
    repeat(pagerState.pageCount) { iteration ->
        val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray
        Box(
            modifier = Modifier
                .padding(2.dp)
                .clip(CircleShape)
                .background(color)
                .size(16.dp)
        )
    }
}

İçeriğin altında daire göstergesi gösteren çağrı cihazı
Şekil 3. İçeriğin altında bir daire göstergesi gösteren çağrı cihazı

İçeriğe öğe kaydırma efektleri uygulama

Yaygın bir kullanım alanı, çağrı cihazı öğelerinize efekt uygulamak için kaydırma konumunu kullanmaktır. Bir sayfanın şu anda seçili olan sayfadan ne kadar uzakta olduğunu öğrenmek için PagerState.currentPageOffsetFraction işlevini kullanabilirsiniz. Ardından, seçilen sayfadan uzaklığa göre içeriğinize dönüşüm efektleri uygulayabilirsiniz.

Şekil 4. Çağrı cihazı içeriğine dönüşüm işlemleri uygulama

Örneğin, öğelerin opaklığını merkezden ne kadar uzakta olduklarına göre ayarlamak için sayfa ayırıcı içindeki bir öğede Modifier.graphicsLayer kullanarak alpha değerini değiştirin:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(state = pagerState) { page ->
    Card(
        Modifier
            .size(200.dp)
            .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 = (
                    (pagerState.currentPage - page) + pagerState
                        .currentPageOffsetFraction
                    ).absoluteValue

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

Özel sayfa boyutları

Varsayılan olarak, HorizontalPager ve VerticalPager sırasıyla tam genişliği veya tam yüksekliği kaplar. pageSize değişkenini Fixed, Fill (varsayılan) veya özel boyut hesaplamasına sahip olacak şekilde ayarlayabilirsiniz.

Örneğin, 100.dp tutarında sabit genişlikli bir sayfa ayarlamak için:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    pageSize = PageSize.Fixed(100.dp)
) { page ->
    // page content
}

Sayfaları görüntü alanı boyutuna göre boyutlandırmak için özel bir sayfa boyutu hesaplaması kullanın. Özel bir PageSize nesnesi oluşturun ve öğeler arasındaki alanı dikkate alarak availableSpace değerini üçe bölün:

private val threePagesPerViewport = object : PageSize {
    override fun Density.calculateMainAxisPageSize(
        availableSpace: Int,
        pageSpacing: Int
    ): Int {
        return (availableSpace - 2 * pageSpacing) / 3
    }
}

İçerik dolgusu

Hem HorizontalPager hem de VerticalPager, içerik dolgusunu değiştirmeyi destekler. Bu sayede sayfaların maksimum boyutunu ve hizalamasını etkileyebilirsiniz.

Örneğin, start dolgusunu ayarlamak sayfaları sona doğru hizalar:

Sona doğru hizalanmış içeriği gösteren, başlangıç dolgulu sayfa cihazı

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(start = 64.dp),
) { page ->
    // page content
}

Hem start hem de end dolgusunun aynı değere ayarlanması öğeyi yatay olarak ortalar:

İçeriği ortalanmış gösteren başlangıç ve bitiş dolgulu sayfa cihazı

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(horizontal = 32.dp),
) { page ->
    // page content
}

end dolgusunu ayarlamak sayfaları başa hizalar:

İçeriği başa hizalı olarak gösteren, başlangıç ve bitiş dolgulu sayfa cihazı

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(end = 64.dp),
) { page ->
    // page content
}

VerticalPager için benzer efektler elde etmek üzere top ve bottom değerlerini ayarlayabilirsiniz. 32.dp değeri burada yalnızca örnek olarak kullanılmıştır. Dolgu boyutlarının her birini herhangi bir değere ayarlayabilirsiniz.

Kaydırma davranışını özelleştirin

Varsayılan HorizontalPager ve VerticalPager composable'ları, kaydırma hareketlerinin çağrı cihazıyla nasıl çalışacağını belirtir. Ancak pagerSnapDistance veya flingBehaviour gibi varsayılanları özelleştirebilir ve değiştirebilirsiniz.

Tutturma mesafesi

Varsayılan olarak HorizontalPager ve VerticalPager, bir sallama hareketinin tek seferde bir sayfayı geçirebileceği maksimum sayfa sayısını ayarlar. Bunu değiştirmek için flingBehavior üzerinde pagerSnapDistance değerini ayarlayın:

val pagerState = rememberPagerState(pageCount = { 10 })

val fling = PagerDefaults.flingBehavior(
    state = pagerState,
    pagerSnapDistance = PagerSnapDistance.atMost(10)
)

Column(modifier = Modifier.fillMaxSize()) {
    HorizontalPager(
        state = pagerState,
        pageSize = PageSize.Fixed(200.dp),
        beyondBoundsPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}