Compose'da Pager

İçerikleri soldan sağa veya yukarıdan aşağıya çevirmek için sırasıyla HorizontalPager ve VerticalPager bileşenlerini kullanabilirsiniz. Bu bileşenler, görüntüleme sistemindeki ViewPager ile benzer işlevlere sahiptir. Varsayılan olarak HorizontalPager, ekranın tam genişliğini kaplar, VerticalPager tüm yüksekliği kaplar ve çağrı cihazları tek seferde yalnızca bir sayfayı hızla gönderir. Bu varsayılanların tümü yapılandırılabilir.

HorizontalPager

Sola ve sağa yatay olarak kaydıran bir sayfalayıcı oluşturmak için HorizontalPagerşunları 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ıran bir sayfalayıcı oluşturmak için VerticalPager simgesini 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()
    )
}

Yavaş oluşturma

Hem HorizontalPager hem de VerticalPager sayfalarındaki sayfalar gecikmeli ve gerektiğinde düzenlenmektedir. Kullanıcı sayfalar arasında gezinirken, artık gerekli olmayan sayfalar bileşimden kaldırılır.

Ekran dışı daha fazla sayfa yükleme

Sayfalayıcı varsayılan olarak 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.

Sayfalayıcıda bir öğeye kaydırma

Sayfalayıcıda belirli bir sayfaya gitmek için rememberPagerState() kullanarak bir PagerState nesnesi oluşturun ve bunu sayfalayıcıya state parametresi olarak iletin. Bu durumda, CoroutineScope içinden PagerState#scrollToPage()'i arayabilirsiniz:

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 alma

PagerState, sayfalar hakkında bilgi içeren üç özelliğe sahiptir: currentPage, settledPage ve targetPage.

  • currentPage: Sabitleme konumuna en yakın sayfa. Varsayılan olarak, sabitleme konumu düzenin başındadır.
  • settledPage: Animasyon veya kaydırma yapılmadığında gösterilen sayfa numarası. Bu, sayfa sabitleme konumuna yeterince yakınsa currentPage hemen güncellenir ancak settledPage tüm animasyonlar tamamlanana kadar aynı kalır. Bu nedenle, currentPage mülkünden farklıdı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 Analytics 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 ekleme

Bir sayfaya gösterge eklemek için sayfa sayısı arasından hangi sayfanın seçildiği hakkında bilgi edinmek amacıyla PagerState nesnesini kullanın ve özel göstergenizi çizin.

Örneğin, basit bir daire göstergesi istiyorsanız 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 sayfalayıcı
Ş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ı, kaydırma konumunu kullanarak sayfalayıcı öğelerinize efekt uygulamaktır. Bir sayfanın seçili olan sayfadan ne kadar uzakta olduğunu öğrenmek için PagerState.currentPageOffsetFraction değerini kullanabilirsiniz. Ardından, seçtiğiniz sayfaya olan mesafeye göre içeriğinize dönüşüm efektleri uygulayabilirsiniz.

Şekil 4. Sayfalayıcı içeriğine dönüşüm 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ı

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

Örneğin, 100.dp sabit genişliğinde 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 boşluğu hesaba katarak 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 doldurma

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

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

İçeriği sona doğru hizalanmış olarak gösteren başlangıç dolgusu içeren sayfalayıcı

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

Hem start hem de end dolgusunu aynı değere ayarlamak, öğeyi yatay olarak ortalar:

İçeriği ortada gösteren başlangıç ve bitiş dolguları içeren sayfalayıcı

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

end dolgusunu ayarlamak sayfaları başlangıca doğru hizalar:

Başlangıç ve bitiş dolguları içeren ve içeriği başlangıca hizalanmış olarak gösteren sayfalayıcı

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ılmaktadır. Dolgu boyutlarının her birini istediğiniz değere ayarlayabilirsiniz.

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

Varsayılan HorizontalPager ve VerticalPager bileşenleri, kaydırma hareketlerinin sayfalayıcıyla nasıl çalıştığını belirtir. Ancak pagerSnapDistance veya flingBehavior gibi varsayılanları özelleştirebilir ve değiştirebilirsiniz.

Kırpma mesafesi

Varsayılan olarak HorizontalPager ve VerticalPager, bir kaydırma hareketinin tek seferde bir sayfayı geçebileceği maksimum sayfa sayısını ayarlar. Bunu değiştirmek için flingBehavior'te 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),
        beyondViewportPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}