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 tüm genişliğini, VerticalPager ise tüm yüksekliğini kaplar ve sayfalayıcılar yalnızca bir sayfayı aynı anda gösterir. 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'teki sayfalar, gerektiğinde tembel bir şekilde derlenir ve düzenlenir. 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çinde PagerState#scrollToPage() değerini çağırabilirsiniz:

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, sayfalarla ilgili bilgiler 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 işleminin yapılmadığı sayfa numarası. Bu, sayfa sabitleme konumuna yeterince yakınsa currentPage hemen güncellenir ancak tüm animasyonlar tamamlanana kadar settledPage 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 PagerState nesnesini kullanarak sayfa sayısı arasından hangi sayfanın seçildiği hakkında bilgi edinin 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 daire göstergesi gösteren sayfalayıcı

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

Kaydırma konumunu, sayfalayıcı öğelerinize efekt uygulamak için kullanmak yaygın bir kullanım alanıdır. Bir sayfanın, seçili sayfaya ne kadar uzak olduğunu öğrenmek için PagerState.currentPageOffsetFraction simgesini 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 uzak olduklarına göre ayarlamak için sayfalayıcıdaki 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 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 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:

İçeriği başlangıca hizalanmış olarak gösteren başlangıç ve bitiş dolgusu içeren 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ılmıştır. Dolgulama boyutlarının her birini istediğiniz değere ayarlayabilirsiniz.

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

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 fırlatma hareketinin tek seferde bir sayfayı atlayabileceğ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)
    }
}