जब पेजों के साथ काम किया हो डेटा न हो, तो आपको अक्सर डेटा स्ट्रीम को लोड करते ही उसे बदल देती हैं. उदाहरण के लिए, आपको Google News पर या आइटम को प्रज़ेंट करने से पहले उसे किसी दूसरे टाइप में बदलें डालें. डेटा स्ट्रीम में बदलाव करने का एक और आम तरीका, सूची जोड़ना है सेपरेटर का इस्तेमाल करें.
आम तौर पर, डेटा स्ट्रीम में सीधे तौर पर ट्रांसफ़ॉर्मेशन ऐक्शन लागू करने पर, आपको का इस्तेमाल करें.
यह पेज मानता है कि आपको पेजिंग के बुनियादी इस्तेमाल के बारे में पता है लाइब्रेरी पर जाएं.
बुनियादी बदलाव लागू करना
क्योंकि PagingData
प्रतिक्रियाशील स्ट्रीम में एनकैप्सुलेट किए गए, तो आप ट्रांसफ़ॉर्म ऑपरेशन को
डेटा को लोड करने और उसे दिखाने के बीच की अवधि को बढ़ाया जा सकता है.
स्ट्रीम में मौजूद हर PagingData
ऑब्जेक्ट में बदलाव लागू करने के लिए,
ट्रांसफ़ॉर्मेशन ऐक्शन को
map()
स्ट्रीम पर कार्रवाई करता है:
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. });
डेटा बदलें
डेटा की स्ट्रीम का सबसे बुनियादी काम है, उसे
टाइप करें. PagingData
ऑब्जेक्ट का ऐक्सेस मिलने के बाद, map()
किया जा सकता है
PagingData
में, पेज वाली सूची में हर आइटम पर कार्रवाई
ऑब्जेक्ट है.
इसका एक सामान्य उपयोग उदाहरण यह है कि एक ऐसा ऑब्जेक्ट जो खास तौर पर यूज़र इंटरफ़ेस (यूआई) लेयर में इस्तेमाल किया गया हो. नीचे दिए गए उदाहरण में बताया गया है कि इस तरह की मैप कार्रवाई लागू करने के लिए:
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) )
उपयोगकर्ता से कोई इनपुट लेना, जैसे कि क्वेरी एक आम डेटा कन्वर्ज़न है स्ट्रिंग में बदलें और उसे दिखाने के लिए अनुरोध आउटपुट में बदलें. इसे सेट अप किया जा रहा है उपयोगकर्ता के क्वेरी इनपुट को सुनने और कैप्चर करने की आवश्यकता होती है, अनुरोध करें और क्वेरी के नतीजे को वापस यूज़र इंटरफ़ेस (यूआई) पर भेजें.
स्ट्रीम एपीआई का इस्तेमाल करके, क्वेरी इनपुट को सुना जा सकता है. स्ट्रीम का रेफ़रंस सेव रखें
आपके ViewModel
में. यूज़र इंटरफ़ेस (यूआई) लेयर की पास इसका सीधा ऐक्सेस नहीं होना चाहिए; इसके बजाय,
उपयोगकर्ता की क्वेरी के ViewModel को सूचित करने के लिए फ़ंक्शन परिभाषित करना होगा.
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) }
डेटा स्ट्रीम में क्वेरी की वैल्यू बदलने पर, ये कार्रवाइयां की जा सकती हैं क्वेरी की वैल्यू को मनचाहे डेटा टाइप में बदलें और नतीजे को यूज़र इंटरफ़ेस (यूआई) में दिखाएं लेयर. कोई कन्वर्ज़न फ़ंक्शन, भाषा और फ़्रेमवर्क पर निर्भर करता है इस्तेमाल किया जाता है, लेकिन वे सभी एक जैसी सुविधाएं देते हैं.
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
या switchMap
जैसी कार्रवाइयों का इस्तेमाल करने पर, यह पक्का होता है कि सिर्फ़
सबसे नए नतीजे, यूज़र इंटरफ़ेस (यूआई) में दिखाए जाते हैं. अगर उपयोगकर्ता अपना क्वेरी इनपुट बदलता है
डेटाबेस की कार्रवाई पूरी होने से पहले, ये कार्रवाइयां नतीजों को खारिज कर देती हैं
और नई खोज को तुरंत लॉन्च करें.
डेटा फ़िल्टर करना
फ़िल्टर करना, एक अन्य सामान्य तरीका है. डेटा को शर्तों के हिसाब से फ़िल्टर किया जा सकता है उपयोगकर्ता से डाउनलोड किया जा सकता है या अगर आपको यूज़र इंटरफ़ेस (यूआई) से डेटा हटाना है, तो का आकलन किया जा सकता है.
आपको ये फ़िल्टर ऑपरेशन map()
कॉल में रखने होंगे, क्योंकि
फ़िल्टर PagingData
ऑब्जेक्ट पर लागू होता है. एक बार डेटा फ़िल्टर कर दिए जाने के बाद
PagingData
, नया PagingData
इंस्टेंस, यूज़र इंटरफ़ेस (यूआई) लेयर को पास किया जाता है
डिसप्ले.
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()) )
सूची सेपरेटर जोड़ें
पेजिंग लाइब्रेरी, डाइनैमिक लिस्ट सेपरेटर के साथ काम करती है. सूची को बेहतर बनाया जा सकता है
डेटा स्ट्रीम में सेपरेटर को सीधे इस तौर पर डालकर आसानी से पढ़ा जा सकता है
सूची में RecyclerView
आइटम हैं. इसलिए, सेपरेटर पूरी तरह से उपलब्ध होते हैं
ViewHolder
ऑब्जेक्ट, जो इंटरैक्टिविटी, सुलभता फ़ोकस, और सभी सुविधाएं चालू करता है
View
की ओर से दी गई अन्य सुविधाएं.
पेज वाली सूची में सेपरेटर को इन तीन चरणों में डाला जा सकता है:
- सेपरेटर आइटम को शामिल करने के लिए, यूज़र इंटरफ़ेस (यूआई) मॉडल को बदलें.
- लोड होने के बीच में सेपरेटर को डाइनैमिक तौर पर जोड़ने के लिए, डेटा स्ट्रीम में बदलाव करें और उस डेटा को दिखाया जा सकता है.
- सेपरेटर आइटम को मैनेज करने के लिए, यूज़र इंटरफ़ेस (यूआई) को अपडेट करें.
यूज़र इंटरफ़ेस (यूआई) मॉडल को बदलना
पेजिंग लाइब्रेरी, RecyclerView
में सूची सेपरेटर को असल के तौर पर इंसर्ट करती है
आइटम की सूची, लेकिन सेपरेटर आइटम, डेटा आइटम से अलग होने चाहिए
ताकि उसे किसी दूसरे ViewHolder
टाइप के साथ बाइंड किया जा सके.
अलग यूज़र इंटरफ़ेस (यूआई) है. इसका समाधान है कि Kotlin की सील हो गई
क्लास
के साथ सब-क्लास का इस्तेमाल करें, ताकि आपके डेटा और सेपरेटर को दिखाया जा सके. इसके अलावा, आपके पास
एक बेस क्लास बना सकता है जिसे आपके लिस्ट आइटम क्लास और आपके
सेपरेटर क्लास का इस्तेमाल करें.
मान लें कि आपको User
आइटम की पेज वाली सूची में सेपरेटर जोड़ना है. कॉन्टेंट बनाने
नीचे दिए गए स्निपेट में, बेस क्लास बनाने का तरीका बताया गया है, जहां इंस्टेंस मौजूद हो सकते हैं
UserModel
या 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; } } }
डेटा स्ट्रीम को पूरी तरह बदलें
आपको डेटा स्ट्रीम को लोड करने के बाद और उससे पहले, उसमें ट्रांसफ़ॉर्मेशन ऐक्शन लागू करने होंगे तो आप उसे प्रज़ेंट करें. ट्रांसफ़ॉर्मेशन में ये कार्रवाइयां की जानी चाहिए:
- नए बेस आइटम टाइप को दिखाने के लिए, लोड की गई सूची के आइटम बदलें.
- सेपरेटर जोड़ने के लिए,
PagingData.insertSeparators()
तरीके का इस्तेमाल करें.
ट्रांसफ़ॉर्मेशन ऑपरेशन के बारे में ज़्यादा जानने के लिए, बेसिक लागू करना देखें ट्रांसफ़ॉर्मेशन.
नीचे दिए गए उदाहरण में, अपडेट करने के लिए ट्रांसफ़ॉर्मेशन ऑपरेशन दिखाए गए हैं
सेपरेटर वाली PagingData<UiModel>
स्ट्रीम में PagingData<User>
स्ट्रीम
जोड़ा गया:
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; } }); });
यूज़र इंटरफ़ेस (यूआई) में सेपरेटर को हैंडल करना
सेपरेटर आइटम टाइप को शामिल करने के लिए, अपने यूज़र इंटरफ़ेस (यूआई) को बदलना आखिरी चरण है.
सेपरेटर आइटम के लिए लेआउट और व्यू होल्डर बनाएं और सूची बदलें
अडैप्टर, RecyclerView.ViewHolder
को इसके व्यू होल्डर टाइप के तौर पर इस्तेमाल करता है, ताकि यह
एक से ज़्यादा टाइप के व्यू होल्डर को हैंडल करने की सुविधा मिलती है. इसके अलावा, आपके पास
बेस क्लास, जिसे आपके आइटम और सेपरेटर व्यू होल्डर, दोनों की क्लास बढ़ाई जाती हैं.
आपको लिस्ट अडैप्टर में ये बदलाव भी करने होंगे:
- इन कामों के लिए,
onCreateViewHolder()
औरonBindViewHolder()
तरीके में केस जोड़ें सेपरेटर सूची आइटम के लिए खाता. - नया तुलना करने वाला टूल लागू करें.
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); } }
डुप्लीकेट काम से बचें
ऐप्लिकेशन से गैर-ज़रूरी काम करने से भी बचा जा सकता है. डाटा लाया जा रहा है और डेटा ट्रांसफ़ॉर्मेशन में भी बहुत ज़्यादा समय लग सकता है. डेटा लोड होने और यूज़र इंटरफ़ेस (यूआई) में दिखाने के लिए तैयार होने के बाद, इसे सेव कर लेना चाहिए कॉन्फ़िगरेशन में बदलाव होने पर और यूज़र इंटरफ़ेस (यूआई) को फिर से बनाने की ज़रूरत होगी.
cachedIn()
कार्रवाई, होने वाले किसी भी बदलाव के नतीजों को कैश मेमोरी में सेव करती है
से पहले. इसलिए, आपके ViewModel में cachedIn()
आख़िरी कॉल होना चाहिए.
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);
PagingData
की स्ट्रीम के साथ cachedIn()
का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, देखें
इसकी स्ट्रीम सेट अप करें
PagingData.
अन्य संसाधन
पेजिंग लाइब्रेरी के बारे में ज़्यादा जानने के लिए, इन अतिरिक्त संसाधनों को देखें:
कोड लैब
सैंपल
- Android आर्किटेक्चर कॉम्पोनेंट पेजिंग सैंपल
- डेटाबेस और नेटवर्क के साथ Android आर्किटेक्चर के कॉम्पोनेंट पेजिंग करना सैंपल
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- पेज किया गया डेटा लोड करना और दिखाना
- पेजिंग लागू करने की प्रक्रिया की जांच करना
- कॉन्टेंट लोड होने की स्थिति को मैनेज करना और उसे प्रज़ेंट करना