Paging kitaplığı, daha büyük bir veri kümesinden sayfalandırılmış verileri yükleme ve görüntüleme için güçlü özellikler sunar. Bu kılavuzda, bir ağ veri kaynağından sayfalandırılmış veri akışı oluşturmak ve bunu lazy list'te görüntülemek için Paging kitaplığının nasıl kullanılacağı gösterilmektedir.
Veri kaynağı tanımlama
İlk adım, veri kaynağını belirlemek için bir PagingSource uygulaması tanımlamaktır. PagingSource API sınıfı, sayfalandırılmış verilerin ilgili veri kaynağından nasıl alınacağını belirtmek için geçersiz kıldığınız load yöntemini içerir.
Asenkron yükleme için Kotlin eş yordamlarını kullanmak üzere PagingSource sınıfını doğrudan kullanın.
Anahtar ve değer türlerini seçme
PagingSource<Key, Value> iki tür parametreye sahiptir: Key ve Value. Anahtar, verileri yüklemek için kullanılan tanımlayıcıyı tanımlar. Değer ise verilerin türüdür. Örneğin, Retrofit'e Int sayfa numaraları ileterek ağdan User nesnelerinin sayfalarını yüklüyorsanız Key türü olarak Int'i, Value türü olarak da User'i seçin.
PagingSource'u tanımlayın
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'dür.
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ı, 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. Yüklenecek anahtar ve yüklenecek öğe sayısı bu parametreye dahildir.
LoadResult nesnesi, yükleme işleminin sonucunu içerir. LoadResult, load çağrısının başarılı olup olmamasına bağlı olarak üç biçimden birini alan kapalı bir sınıftır:
- Yükleme başarılı olursa
LoadResult.Pagenesnesi döndürülür. - Yükleme başarılı olmazsa
LoadResult.Errornesnesi döndürün. PagingSourceartık geçerli değilse ve yeni bir örnekle değiştirilmesi gerekiyorsa (örneğin, temel verilerde değişiklik yapıldığından)LoadResult.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 öğesinin anahtarı nasıl kullandığını ve güncellediğini gösteren diyagram.
PagingSource uygulaması, PagingState nesnesini parametre olarak alan bir getRefreshKey yöntemini de uygulamalıdır. İlk yüklemeden sonra veriler 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.
Hataları işleme
Veri yükleme istekleri, özellikle ağ üzerinden yükleme yapılırken ç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, ExamplePagingSource yöntemine aşağıdakileri ekleyerek önceki örnekteki load 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ı işleme hakkında daha fazla bilgi için PagingSource API referansındaki örneklere bakın.
PagingSource, LoadResult.Error nesnelerini toplar ve kullanıcı arayüzüne iletir. 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 alınan, sayfalandırılmış bir veri akışına ihtiyacınız vardır.
ViewModel uygulamanızda veri akışını ayarlayın. Pager sınıfı, PagingSource kaynağından PagingData nesnelerinin reaktif akışını gösteren yöntemler sağlar. Paging kitaplığı, veri akışını Flow olarak kullanıma sunar.
Reaktif akışınızı ayarlamak için bir Pager örneği oluşturduğunuzda, aşağıdaki örnekte gösterildiği gibi, örneğe bir PagingConfig yapılandırma nesnesi ve Pager öğesine PagingSource uygulamanızın bir örneğini nasıl alacağını söyleyen bir işlev sağlamanız gerekir.
class UserViewModel(
private val backend: ExampleBackendService,
private val query: String
) : ViewModel() {
val userPagingFlow: Flow<PagingData<User>> = Pager(
// Configure how data is loaded by passing additional properties to
// PagingConfig, such as pageSize and enabling or disabling placeholders.
config = PagingConfig(
pageSize = 20,
enablePlaceholders = true
),
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
yeniden toplanamaz. Bu örnekte, yaşam döngüsü lifecycle-viewmodel-ktx yapıtı 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ın ve kullanıcı arayüzünüzde gösterin
Sayfalandırılmış akışı kullanıcı arayüzüne bağlamak için akışı ViewModel öğenizden alın ve liste composable'ınıza iletin.
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
val userFlow = viewModel.userPagingFlow
UserList(flow = userFlow)
}
PagingData akışını LazyPagingItems akışına dönüştürmek için collectAsLazyPagingItems öğesini kullanın. Ardından, her öğeyi yerleştirmek için LazyColumn içinde items API'sini kullanın.
itemKey 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 boyunca User örneği için sabit kaldığından it.id (User.id mülküne referans vererek) kullanılı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()
}
}
}
}
Paging 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.
Liste artık sayfalandırılmış verileri gösteriyor ve kullanıcı kaydırdıkça Paging kitaplığı ek sayfalar yüklüyor.
Ek kaynaklar
Paging kitaplığı hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:
Belgeler
- Sayfalama (Paging)
İçeriği görüntüleme
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Ağ ve veritabanından sayfa
- Paging 3'e geçiş
- Sayfa ayırma kitaplığına genel bakış