Sayfalanmış verileri kullanıyorsanız genellikle veri akışını dönüştürebilirsiniz. Örneğin, her ekip üyesinin veya sunumdan önce öğeleri farklı bir türe dönüştürerek kullanıcı arayüzü. Veri akışı dönüştürme için bir diğer yaygın kullanım alanı da liste, ayırıcılar ile kullanılabilir.
Daha genel olarak ifade etmek gerekirse dönüşümleri doğrudan veri akışına uygulayarak ile birlikte kod deposu ve kullanıcı arayüzü yapılarınızı ayrı tutun.
Bu sayfada, Numaralandırma Aracı'nın temel kullanımıyla ilgili bilginiz olduğu varsayılır. kitaplığı'nda bulabilirsiniz.
Temel dönüşümleri uygulama
Çünkü PagingData
bir akışa yerleştirilmiş olarak, dönüşüm işlemlerini
ve sunma aşamaları arasında otomatik olarak geçiş yapmayı
unutmayın.
Akıştaki her PagingData
nesnesine dönüşüm uygulamak için
dönüşümleri bir
map()
akışta işlem:
Kotlin
pager.flow // Type is Flow<PagingData<User>>. // Map the outer stream so that the transformations are applied to // each new generation of PagingData. .map { pagingData -> // Transformations in this block are applied to the items // in the paged data. }
Java
PagingRx.getFlowable(pager) // Type is Flowable<PagingData<User>>. // Map the outer stream so that the transformations are applied to // each new generation of PagingData. .map(pagingData -> { // Transformations in this block are applied to the items // in the paged data. });
Java
// Map the outer stream so that the transformations are applied to // each new generation of PagingData. Transformations.map( // Type is LiveData<PagingData<User>>. PagingLiveData.getLiveData(pager), pagingData -> { // Transformations in this block are applied to the items // in the paged data. });
Verileri dönüştürme
Bir veri akışındaki en temel işlem, veri akışındaki
türü. PagingData
nesnesine erişim elde ettikten sonra bir map()
gerçekleştirebilirsiniz
PagingData
içindeki sayfalı listedeki her bir bağımsız öğe için işlem
nesnesini tanımlayın.
Bunun yaygın kullanım alanlarından biri, ağ veya veritabanı katmanı nesnesini özellikle kullanıcı arayüzü katmanında kullanılan bir nesnedir. Aşağıdaki örnekte, şu harita işlemi türünü uygulamak için:
Kotlin
pager.flow // Type is Flow<PagingData<User>>. .map { pagingData -> pagingData.map { user -> UiModel(user) } }
Java
// Type is Flowable<PagingData<User>>. PagingRx.getFlowable(pager) .map(pagingData -> pagingData.map(UiModel.UserModel::new) )
Java
Transformations.map( // Type is LiveData<PagingData<User>>. PagingLiveData.getLiveData(pager), pagingData -> pagingData.map(UiModel.UserModel::new) )
Yaygın olarak kullanılan bir diğer veri dönüşümü ise kullanıcıdan sorgu gibi bir giriş almaktır. dizesine dönüştürülebilir ve görüntülenecek istek çıkışına dönüştürülebilir. Bunu ayarlama kullanıcının sorgu girişini dinlemeyi ve yakalamayı gerektirir. isteğinde bulunmak ve sorgu sonucunu kullanıcı arayüzüne geri aktarmaktır.
Akış API'si kullanarak sorgu girişini dinleyebilirsiniz. Akış referansını muhafaza et
ViewModel
içinde. Kullanıcı arayüzü katmanına doğrudan erişimi olmamalıdır, bunun yerine
kullanıcı sorgusunun ViewModel'ini bildirmek için bir işlev tanımlayın.
Kotlin
private val queryFlow = MutableStateFlow("") fun onQueryChanged(query: String) { queryFlow.value = query }
Java
private BehaviorSubject<String> querySubject = BehaviorSubject.create(""); public void onQueryChanged(String query) { queryFlow.onNext(query) }
Java
private MutableLiveData<String> queryLiveData = new MutableLiveData(""); public void onQueryChanged(String query) { queryFlow.setValue(query) }
Veri akışındaki sorgu değeri değiştiğinde, sorgu değerini istenen veri türüne dönüştürür ve sonucu kullanıcı arayüzüne döndürür katmanıdır. Özel dönüştürme işlevi, kullandığınız dile ve çerçeveye ancak hepsi benzer işlevler sunuyor.
Kotlin
val querySearchResults = queryFlow.flatMapLatest { query -> // The database query returns a Flow which is output through // querySearchResults userDatabase.searchBy(query) }
Java
Observable<User> querySearchResults = querySubject.switchMap(query -> userDatabase.searchBy(query));
Java
LiveData<User> querySearchResults = Transformations.switchMap( queryLiveData, query -> userDatabase.searchBy(query) );
flatMapLatest
veya switchMap
gibi işlemler kullandığınızda yalnızca
en son sonuçlar kullanıcı arayüzüne döndürülür. Kullanıcı sorgu girişini değiştirirse
veritabanı işlemi tamamlanmadan önce bu işlemler sonuçları siler
çıkarıp yeni aramayı hemen başlatabilirsiniz.
Verileri filtrele
Sık yapılan diğer bir işlem de filtrelemedir. Verileri ölçütlere göre filtreleyebilirsiniz veya gizlenmesi gereken verileri kullanıcı arayüzünden kaldırabilirsiniz. başka ölçütlere göre yapılır.
Bu filtre işlemlerini map()
çağrısının içine yerleştirmeniz gerekir, çünkü
PagingData
nesnesine uygulanır. Veriler filtrelenerek
PagingData
, yeni PagingData
örneği kullanıcı arayüzü katmanına
görüntüleyin.
Kotlin
pager.flow // Type is Flow<PagingData<User>>. .map { pagingData -> pagingData.filter { user -> !user.hiddenFromUi } }
Java
// Type is Flowable<PagingData<User>>. PagingRx.getFlowable(pager) .map(pagingData -> pagingData.filter(user -> !user.isHiddenFromUi()) ) }
Java
Transformations.map( // Type is LiveData<PagingData<User>>. PagingLiveData.getLiveData(pager), pagingData -> pagingData.filter(user -> !user.isHiddenFromUi()) )
Liste ayırıcıları ekleyin
Sayfalama kitaplığı dinamik liste ayırıcılarını destekler. Listeyi iyileştirebilirsiniz
doğrudan veri akışına ayırıcı ekleyerek okunabilirliği
RecyclerView
liste öğesi. Sonuç olarak, ayırıcılar tam olarak
ViewHolder
nesneleri sayesinde etkileşim, erişilebilirlik odağı ve
View
tarafından sağlanan diğer özellikler.
Sayfalı listenize ayırıcılar eklemek için izlemeniz gereken üç adım vardır:
- Ayırıcı öğeleri içerecek şekilde kullanıcı arayüzü modelini dönüştürün.
- Yükleme arasına ayırıcıları dinamik olarak eklemek için veri akışını dönüştürün verileri sunmaktan ibaret değildir.
- Ayırıcı öğeleri işleyecek şekilde kullanıcı arayüzünü güncelleyin.
Kullanıcı arayüzü modelini dönüştürme
Sayfalama kitaplığı, liste ayırıcıları RecyclerView
içine gerçek olarak ekler
ancak ayırıcı öğeler, veri öğelerinden ayırt edilebilmelidir
farklı bir ViewHolder
türüne bağlanmalarını sağlamak için
ayrı bir kullanıcı arayüzü sunar. Çözüm, Kotlin mühürlü bir
sınıf
. Alternatif olarak
liste öğesi sınıfınız ve kampanyalarınıza göre genişletilen bir temel sınıf oluşturabilirsiniz.
ayırıcı sınıfıdır.
User
öğeden oluşan sayfalı bir listeye ayırıcılar eklemek istediğinizi varsayalım. İlgili içeriği oluşturmak için kullanılan
aşağıdaki snippet, örneklerin kullanılabileceği bir temel sınıfın nasıl oluşturulacağını gösterir
bir UserModel
veya SeparatorModel
:
Kotlin
sealed class UiModel { class UserModel(val id: String, val label: String) : UiModel() { constructor(user: User) : this(user.id, user.label) } class SeparatorModel(val description: String) : UiModel() }
Java
class UiModel { private UiModel() {} static class UserModel extends UiModel { @NonNull private String mId; @NonNull private String mLabel; UserModel(@NonNull String id, @NonNull String label) { mId = id; mLabel = label; } UserModel(@NonNull User user) { mId = user.id; mLabel = user.label; } @NonNull public String getId() { return mId; } @NonNull public String getLabel() { return mLabel; } } static class SeparatorModel extends UiModel { @NonNull private String mDescription; SeparatorModel(@NonNull String description) { mDescription = description; } @NonNull public String getDescription() { return mDescription; } } }
Java
class UiModel { private UiModel() {} static class UserModel extends UiModel { @NonNull private String mId; @NonNull private String mLabel; UserModel(@NonNull String id, @NonNull String label) { mId = id; mLabel = label; } UserModel(@NonNull User user) { mId = user.id; mLabel = user.label; } @NonNull public String getId() { return mId; } @NonNull public String getLabel() { return mLabel; } } static class SeparatorModel extends UiModel { @NonNull private String mDescription; SeparatorModel(@NonNull String description) { mDescription = description; } @NonNull public String getDescription() { return mDescription; } } }
Veri akışını dönüştürme
Veri akışını yükledikten sonra ve öncesinde dönüştürme işlemleri uygulamanız gerekir. göstermeniz gerekir. Dönüşüm işlemleri aşağıdaki işlemleri yapmalıdır:
- Yüklenen liste öğelerini, yeni temel öğe türünü yansıtacak şekilde dönüştürün.
- Ayırıcıları eklemek için
PagingData.insertSeparators()
yöntemini kullanın.
Dönüştürme işlemleri hakkında daha fazla bilgi edinmek için Temel bilgileri uygulama dönüşümleri hakkında daha fazla bilgi edinin.
Aşağıdaki örnekte
Ayırıcılar içeren PagingData<UiModel>
akışına PagingData<User>
akışı
eklendi:
Kotlin
pager.flow.map { pagingData: PagingData<User> -> // Map outer stream, so you can perform transformations on // each paging generation. pagingData .map { user -> // Convert items in stream to UiModel.UserModel. UiModel.UserModel(user) } .insertSeparators<UiModel.UserModel, UiModel> { before, after -> when { before == null -> UiModel.SeparatorModel("HEADER") after == null -> UiModel.SeparatorModel("FOOTER") shouldSeparate(before, after) -> UiModel.SeparatorModel( "BETWEEN ITEMS $before AND $after" ) // Return null to avoid adding a separator between two items. else -> null } } }
Java
// Map outer stream, so you can perform transformations on each // paging generation. PagingRx.getFlowable(pager).map(pagingData -> { // First convert items in stream to UiModel.UserModel. PagingData<UiModel> uiModelPagingData = pagingData.map( UiModel.UserModel::new); // Insert UiModel.SeparatorModel, which produces PagingData of // generic type UiModel. return PagingData.insertSeparators(uiModelPagingData, (@Nullable UiModel before, @Nullable UiModel after) -> { if (before == null) { return new UiModel.SeparatorModel("HEADER"); } else if (after == null) { return new UiModel.SeparatorModel("FOOTER"); } else if (shouldSeparate(before, after)) { return new UiModel.SeparatorModel("BETWEEN ITEMS " + before.toString() + " AND " + after.toString()); } else { // Return null to avoid adding a separator between two // items. return null; } }); });
Java
// Map outer stream, so you can perform transformations on each // paging generation. Transformations.map(PagingLiveData.getLiveData(pager), pagingData -> { // First convert items in stream to UiModel.UserModel. PagingData<UiModel> uiModelPagingData = pagingData.map( UiModel.UserModel::new); // Insert UiModel.SeparatorModel, which produces PagingData of // generic type UiModel. return PagingData.insertSeparators(uiModelPagingData, (@Nullable UiModel before, @Nullable UiModel after) -> { if (before == null) { return new UiModel.SeparatorModel("HEADER"); } else if (after == null) { return new UiModel.SeparatorModel("FOOTER"); } else if (shouldSeparate(before, after)) { return new UiModel.SeparatorModel("BETWEEN ITEMS " + before.toString() + " AND " + after.toString()); } else { // Return null to avoid adding a separator between two // items. return null; } }); });
Kullanıcı arayüzündeki tutma yeri ayırıcıları
Son adım, kullanıcı arayüzünü ayırıcı öğe türüne uygun olacak şekilde değiştirmektir.
Ayırıcı öğeleriniz için düzen ve görünüm tutucu oluşturup listeyi değiştirme
özelliğini kullanarak RecyclerView.ViewHolder
adlı bir bağdaştırıcıyı
birden çok görünüm sahibini ele alabilir. Alternatif olarak, projede
hem öğe hem de ayırıcı görünüm sahibi sınıflarının genişletildiği temel sınıfları kullanın.
Liste bağdaştırıcınızda aşağıdaki değişiklikleri de yapmanız gerekir:
onCreateViewHolder()
veonBindViewHolder()
yöntemlerine vaka ekleyin: ayırıcı liste öğelerini hesaba katar.- Yeni bir karşılaştırıcı uygulama.
Kotlin
class UiModelAdapter : PagingDataAdapter<UiModel, RecyclerView.ViewHolder>(UiModelComparator) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ) = when (viewType) { R.layout.item -> UserModelViewHolder(parent) else -> SeparatorModelViewHolder(parent) } override fun getItemViewType(position: Int) { // Use peek over getItem to avoid triggering page fetch / drops, since // recycling views is not indicative of the user's current scroll position. return when (peek(position)) { is UiModel.UserModel -> R.layout.item is UiModel.SeparatorModel -> R.layout.separator_item null -> throw IllegalStateException("Unknown view") } } override fun onBindViewHolder( holder: RecyclerView.ViewHolder, position: Int ) { val item = getItem(position) if (holder is UserModelViewHolder) { holder.bind(item as UserModel) } else if (holder is SeparatorModelViewHolder) { holder.bind(item as SeparatorModel) } } } object UiModelComparator : DiffUtil.ItemCallback<UiModel>() { override fun areItemsTheSame( oldItem: UiModel, newItem: UiModel ): Boolean { val isSameRepoItem = oldItem is UiModel.UserModel && newItem is UiModel.UserModel && oldItem.id == newItem.id val isSameSeparatorItem = oldItem is UiModel.SeparatorModel && newItem is UiModel.SeparatorModel && oldItem.description == newItem.description return isSameRepoItem || isSameSeparatorItem } override fun areContentsTheSame( oldItem: UiModel, newItem: UiModel ) = oldItem == newItem }
Java
class UiModelAdapter extends PagingDataAdapter<UiModel, RecyclerView.ViewHolder> { UiModelAdapter() { super(new UiModelComparator(), Dispatchers.getMain(), Dispatchers.getDefault()); } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == R.layout.item) { return new UserModelViewHolder(parent); } else { return new SeparatorModelViewHolder(parent); } } @Override public int getItemViewType(int position) { // Use peek over getItem to avoid triggering page fetch / drops, since // recycling views is not indicative of the user's current scroll position. UiModel item = peek(position); if (item instanceof UiModel.UserModel) { return R.layout.item; } else if (item instanceof UiModel.SeparatorModel) { return R.layout.separator_item; } else { throw new IllegalStateException("Unknown view"); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceOf UserModelViewHolder) { UserModel userModel = (UserModel) getItem(position); ((UserModelViewHolder) holder).bind(userModel); } else { SeparatorModel separatorModel = (SeparatorModel) getItem(position); ((SeparatorModelViewHolder) holder).bind(separatorModel); } } } class UiModelComparator extends DiffUtil.ItemCallback<UiModel> { @Override public boolean areItemsTheSame(@NonNull UiModel oldItem, @NonNull UiModel newItem) { boolean isSameRepoItem = oldItem instanceof UserModel && newItem instanceof UserModel && ((UserModel) oldItem).getId().equals(((UserModel) newItem).getId()); boolean isSameSeparatorItem = oldItem instanceof SeparatorModel && newItem instanceof SeparatorModel && ((SeparatorModel) oldItem).getDescription().equals( ((SeparatorModel) newItem).getDescription()); return isSameRepoItem || isSameSeparatorItem; } @Override public boolean areContentsTheSame(@NonNull UiModel oldItem, @NonNull UiModel newItem) { return oldItem.equals(newItem); } }
Java
class UiModelAdapter extends PagingDataAdapter<UiModel, RecyclerView.ViewHolder> { UiModelAdapter() { super(new UiModelComparator(), Dispatchers.getMain(), Dispatchers.getDefault()); } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == R.layout.item) { return new UserModelViewHolder(parent); } else { return new SeparatorModelViewHolder(parent); } } @Override public int getItemViewType(int position) { // Use peek over getItem to avoid triggering page fetch / drops, since // recycling views is not indicative of the user's current scroll position. UiModel item = peek(position); if (item instanceof UiModel.UserModel) { return R.layout.item; } else if (item instanceof UiModel.SeparatorModel) { return R.layout.separator_item; } else { throw new IllegalStateException("Unknown view"); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceOf UserModelViewHolder) { UserModel userModel = (UserModel) getItem(position); ((UserModelViewHolder) holder).bind(userModel); } else { SeparatorModel separatorModel = (SeparatorModel) getItem(position); ((SeparatorModelViewHolder) holder).bind(separatorModel); } } } class UiModelComparator extends DiffUtil.ItemCallback<UiModel> { @Override public boolean areItemsTheSame(@NonNull UiModel oldItem, @NonNull UiModel newItem) { boolean isSameRepoItem = oldItem instanceof UserModel && newItem instanceof UserModel && ((UserModel) oldItem).getId().equals(((UserModel) newItem).getId()); boolean isSameSeparatorItem = oldItem instanceof SeparatorModel && newItem instanceof SeparatorModel && ((SeparatorModel) oldItem).getDescription().equals( ((SeparatorModel) newItem).getDescription()); return isSameRepoItem || isSameSeparatorItem; } @Override public boolean areContentsTheSame(@NonNull UiModel oldItem, @NonNull UiModel newItem) { return oldItem.equals(newItem); } }
Yinelenen işlerden kaçının
Kaçınılması gereken önemli sorunlardan biri, uygulamanın gereksiz işler yapmasını sağlamaktır. Veri getiriliyor: çok maliyetli bir işlemdir ve veri dönüştürme işlemleri de değerli zaman alabilir. Veriler yüklenip kullanıcı arayüzünde görüntülenmek üzere hazırlandıktan sonra kaydedilecektir. ve kullanıcı arayüzünün yeniden oluşturulması gerekebilir.
cachedIn()
işlemi, gerçekleşen tüm dönüşümlerin sonuçlarını önbelleğe alır
girin. Bu nedenle, cachedIn()
, ViewModel'inizdeki son çağrı olmalıdır.
Kotlin
pager.flow // Type is Flow<PagingData<User>>. .map { pagingData -> pagingData.filter { user -> !user.hiddenFromUi } .map { user -> UiModel.UserModel(user) } } .cachedIn(viewModelScope)
Java
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact. CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel); PagingRx.cachedIn( // Type is Flowable<PagingData<User>>. PagingRx.getFlowable(pager) .map(pagingData -> pagingData .filter(user -> !user.isHiddenFromUi()) .map(UiModel.UserModel::new)), viewModelScope); }
Java
// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact. CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel); PagingLiveData.cachedIn( Transformations.map( // Type is LiveData<PagingData<User>>. PagingLiveData.getLiveData(pager), pagingData -> pagingData .filter(user -> !user.isHiddenFromUi()) .map(UiModel.UserModel::new)), viewModelScope);
cachedIn()
uygulamasını PagingData
akışıyla kullanma hakkında daha fazla bilgi için bkz.
Şu etkinlikten oluşan bir akış kur:
PagingData.
Ek kaynaklar
Sayfalama kitaplığı hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:
Codelab'ler
Örnekler
- Android Mimari Bileşenleri Sayfalandırması örnek
- Veritabanı ve Ağ ile Android Mimari Bileşenleri Sayfalandırması örnek
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Sayfalandırılmış verileri yükleme ve görüntüleme
- Sayfalama uygulamanızı test etme
- Yükleme durumlarını yönetme ve gösterme