Paging kitaplığı, daha büyük bir veri kümesinden sayfalanmış verileri yüklemek ve görüntülemek için güçlü özellikler sunar. Bu kılavuzda, bir ağ veri kaynağından sayfalanmış veri akışı oluşturmak ve bunu gecikmeli listede göstermek için Paging kitaplığının nasıl kullanılacağı gösterilmektedir.
Veri kaynağı tanımlama
İlk adım, veri kaynağını tanımlamak için bir PagingSource uygulaması tanımlamaktır. PagingSource API sınıfı, ilgili veri kaynağından sayfalanmış verilerin nasıl alınacağını belirtmek için geçersiz kıldığınız load yöntemini içerir.
Eşzamansız yükleme için Kotlin eş yordamlarını kullanmak istiyorsanız doğrudan PagingSource sınıfını kullanın.
Anahtar ve değer türlerini seçme
PagingSource<Key, Value> iki tür parametreye sahip: Key ve Value. Anahtar, verileri yüklemek için kullanılan tanımlayıcıyı belirtir. Değer ise verilerin türüdür. Örneğin, Retrofit'e Int sayfa numarası ileterek ağdan User nesne sayfaları yüklüyorsanız Key türü olarak Int, Value türü olarak User seçin.
PagingSource'u tanımlama
Aşağıdaki örnekte, öğe sayfalarını sayfa numarasına göre yükleyen bir PagingSource uygulanmaktadır. Key türü Int, Value türü ise User.
class ExamplePagingSource(
val backend: ExampleBackendService,
val query: String
) : PagingSource<Int, User>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, User> {
init {
// the data source is expected to be immutable
// invalidate PagingSource if data source
// has updated
backEnd.addDatabaseOnChangedListener {
invalidate()
}
}
try {
// Start refresh at page 1 if undefined.
val nextPageNumber = params.key ?: 1
val response = backend.searchUsers(query, nextPageNumber)
return LoadResult.Page(
data = response.users,
prevKey = null, // Only paging forward.
nextKey = nextPageNumber + 1
)
} catch (e: Exception) {
// Handle errors in this block and return LoadResult.Error for
// expected errors (such as a network failure).
}
}
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
// Try to find the page key of the closest page to anchorPosition from
// either the prevKey or the nextKey; you need to handle nullability
// here.
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey are null -> anchorPage is the
// initial page, so return null.
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
Tipik bir PagingSource uygulaması, bir sorgu için uygun verileri yüklemek üzere oluşturucusunda sağlanan parametreleri load yöntemine iletir. Yukarıdaki örnekte bu parametreler şunlardır:
backend: Verileri sağlayan arka uç hizmetinin bir örneğiquery:backendile belirtilen hizmete gönderilecek arama sorgusu
LoadParams nesnesi, gerçekleştirilecek yükleme işlemiyle ilgili bilgileri içerir. Bu, yüklenecek anahtarı ve yüklenecek öğe sayısını içerir. load içindeki yükleme türlerini ayırt etmek için hangi LoadParams alt sınıfının iletildiğini kontrol edin: LoadParams.Refresh, LoadParams.Append veya LoadParams.Prepend.
LoadResult nesnesi, yükleme işleminin sonucunu içerir. LoadResult, load çağrısının başarılı olup olmadığına bağlı olarak üç biçimden birini alan kapalı bir sınıftır:
- Yükleme başarılı olursa bir
LoadResult.Pagenesnesi döndürün. Yönlerden birinde daha fazla veri yüklenemiyorsanextKeyveyaprevKeyiçinnulldeğerini iletin. - Yükleme başarılı olmazsa bir
LoadResult.Errornesnesi döndürün. - Temel veriler değiştiği için
PagingSourceartık geçerli değilseLoadResult.Invalidnesnesi döndürün.
Aşağıdaki şekilde, bu örnekteki load işlevinin her yükleme için anahtarı nasıl aldığı ve sonraki yükleme için anahtarı nasıl sağladığı gösterilmektedir.
load hizmetinin anahtarı nasıl kullandığını ve güncellediğini gösteren şema.
PagingSource uygulamasının, parametre olarak bir PagingState nesnesi alan bir getRefreshKey yöntemini de uygulaması gerekir. Veriler ilk yüklemeden sonra yenilendiğinde veya geçersiz kılındığında load yöntemine iletilecek anahtarı döndürür. Paging kitaplığı, verilerin sonraki yenilemelerinde bu yöntemi otomatik olarak çağırır. Anahtarların öğe konumlarını temsil ettiği, ofset tabanlı sayfalama için yenileme anahtarını state.anchorPosition değerine göre hesaplayabilirsiniz:
// Only for positional or offset PagingSources where Key is a 0-based item index
// rather than a page number.
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
// Center the refreshed load around the user's current viewport so the list
// does not jump back to the top on invalidation.
return state.anchorPosition?.let { anchorPosition ->
(anchorPosition - state.config.initialLoadSize / 2).coerceAtLeast(0)
}
}
Hataları işleme
Veri yükleme istekleri, özellikle ağ üzerinden yükleme yaparken çeşitli nedenlerle başarısız olabilir. load yönteminden bir LoadResult.Error nesnesi döndürerek yükleme sırasında karşılaşılan hataları bildirin.
Örneğin, load yöntemine aşağıdakileri ekleyerek önceki örnekteki ExamplePagingSource
yükleme hatalarını yakalayıp bildirebilirsiniz:
catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
}
Retrofit hatalarını giderme hakkında daha fazla bilgi için PagingSource API referansındaki örneklere bakın.
PagingSource, LoadResult.Error nesneleri toplayıp kullanıcı arayüzüne gönderir. Böylece bu nesneler üzerinde işlem yapabilirsiniz. Yükleme durumunu kullanıcı arayüzünde gösterme hakkında daha fazla bilgi için Yükleme durumlarını yönetme ve sunma başlıklı makaleyi inceleyin.
PagingData akışı oluşturma
Ardından, PagingSource uygulamasından sayfalandırılmış veri akışına ihtiyacınız vardır.
ViewModel uygulamanızda veri akışını ayarlayın. Pager sınıfı, PagingSource'dan gelen PagingData nesnelerinin reaktif akışını gösteren yöntemler sağlar. Paging kitaplığı, veri akışını Flow olarak gösterir.
Tepkisel akışınızı ayarlamak için bir Pager örneği oluşturduğunuzda örneğe bir PagingConfig yapılandırma nesnesi ve Pager'e PagingSource uygulamanızın bir örneğini nasıl alacağını bildiren bir işlev sağlamanız gerekir. İsteğe bağlı olarak initialKey da belirtebilirsiniz. Örneğin, 1 tabanlı sayfa numaraları için 1 veya ofset tabanlı sayfalama için 0 kullanabilirsiniz. PagingConfig'da pageSize, initialLoadSize, prefetchDistance ve enablePlaceholders'yı yapılandırabilirsiniz. prefetchDistance özelliği, yüklenen içeriğin her iki kenarından kaç öğenin bir sonraki getirme işlemini tetikleyeceğini tanımlar.
class UserViewModel(
private val backend: ExampleBackendService,
private val query: String
) : ViewModel() {
val userPagingFlow: Flow<PagingData<User>> = Pager(
config = PagingConfig(
pageSize = 20,
// Load 3 pages initially so the user can scroll immediately without
// hitting a loading state.
initialLoadSize = 60,
// Fetch the next page far enough ahead of the scroll position to
// avoid visible pauses.
prefetchDistance = 20,
enablePlaceholders = true
),
initialKey = 1,
pagingSourceFactory = {
ExamplePagingSource(backend, query)
}
)
.flow
.cachedIn(viewModelScope)
}
cachedIn operatörü, veri akışını paylaşılabilir hale getirir ve yüklenen verileri sağlanan CoroutineScope ile önbelleğe alır. cachedIn olmadan PagingData
hatırlanamaz. Bu örnekte, yaşam döngüsü lifecycle-viewmodel-ktx yapısı tarafından sağlanan viewModelScope kullanılmaktadır.
Pager nesnesi, PagingSource nesnesinden load yöntemini çağırarak LoadParams nesnesini sağlar ve karşılığında LoadResult nesnesini alır.
Verileri toplayıp kullanıcı arayüzünüzde gösterme
Sayfalı akışı kullanıcı arayüzüne bağlamak için akışı ViewModel öğenizden alıp liste composable'ınıza iletin.
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
val userFlow = viewModel.userPagingFlow
UserList(flow = userFlow)
}
PagingData akışını LazyPagingItems biçimine dönüştürmek için collectAsLazyPagingItems kullanın. Ardından, her bir öğeyi düzenlemek için LazyColumn içinde items API'sini kullanın.
itemKey özelliğini kullanarak her öğe için benzersiz ve sabit bir tanımlayıcı sağladığınızdan emin olun.
Aşağıdaki örnekte, veri güncellemeleri sırasında User örneği için sabit kaldığından it.id (User.id mülküne referans vererek) kullanılmaktadır.
@Composable
fun UserList(flow: Flow<PagingData<User>>) {
val lazyPagingItems = flow.collectAsLazyPagingItems()
LazyColumn {
items(
lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.id }
) { index ->
val user = lazyPagingItems[index]
if (user != null) {
UserRow(user)
} else {
UserPlaceholder()
}
}
}
}
Sayfalama kitaplığı, bir sayfa yüklenirken yer tutucular için null kullanır. Bu nedenle, yer tutucuları etkinleştirdiyseniz içerik bloğundaki null değerlerini işlemeniz gerekir.
Artık listede sayfalanmış veriler gösterilir ve kullanıcı sayfayı kaydırdıkça Paging kitaplığı ek sayfalar yükler.
Ek kaynaklar
Paging kitaplığı hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın:
Belgeler
İçerik görüntüleme
Sizin için önerilenler
- Not: JavaScript devre dışı olduğunda bağlantı metni gösterilir.
- Ağ ve veritabanından sayfa oluşturma
- Paging 3'e geçiş yapma
- Paging kitaplığına genel bakış