android - बनत - पुनर्चक्रण क्या है
पेजिंग के लिए प्रोग्रेसबार के साथ अंतहीन पुनर्नवीकरण (6)
मैं एक
RecyclerView
का उपयोग कर रहा हूं और दस के बैच में API से ऑब्जेक्ट ला रहा हूं।
EndlessRecyclerOnScrollListener
लिए, मैं
EndlessRecyclerOnScrollListener
उपयोग करता
EndlessRecyclerOnScrollListener
।
यह सब ठीक से काम कर रहा है।
अब जो कुछ बचा है वह सूची के निचले भाग में एक प्रगति स्पिनर को जोड़ना है जबकि वस्तुओं के अगले बैच को एपीआई द्वारा प्राप्त किया जाता है।
यहां Google Play Store ऐप का एक स्क्रीनशॉट है, जो निश्चित रूप से एक
RecyclerView
दिखा रहा है, जो कि
RecyclerView
:
समस्या यह है कि न तो
RecyclerView
और न ही
EndlessRecyclerOnScrollListener
ने नीचे की तरफ एक
ProgressBar
दिखाने के लिए बिल्ट-इन सपोर्ट दिया है, जबकि ऑब्जेक्ट्स का अगला बैच लाया जा रहा है।
मैंने पहले ही निम्नलिखित उत्तर देख लिए हैं:
1.
एक
RecyclerView
एक
RecyclerView
ग्रिड में पाद लेख के रूप में रखो
।
2.
निचले स्तर पर
ProgressBar
साथ अंतहीन स्क्रॉल
RecyclerView
आइटम जोड़ना
।
मैं उन उत्तरों (एक ही व्यक्ति द्वारा दोनों) से संतुष्ट नहीं हूं।
इसमें उपयोगकर्ता द्वारा स्क्रॉल करते समय डेटा-सेट मिडवे में एक
null
ऑब्जेक्ट को जूता करना शामिल है और अगले बैच के वितरित होने के बाद इसे बाहर निकालना।
यह एक हैक की तरह दिखता है जो मुख्य समस्या को दूर करता है जो ठीक से काम नहीं कर सकता है या नहीं।
और यह सूची में थोड़ी गड़बड़ी और विकृति का कारण बनता है
SwipeRefreshLayout
का उपयोग करना यहां कोई समाधान नहीं है।
SwipeRefreshLayout
में
नए
आइटम लाने के लिए ऊपर से खींचना शामिल है, और यह वैसे भी प्रगति दृश्य नहीं दिखाता है।
क्या कोई इसके लिए एक अच्छा समाधान प्रदान कर सकता है? मुझे यह जानने में दिलचस्पी है कि Google ने अपने स्वयं के ऐप्स के लिए इसे कैसे लागू किया है (जीमेल ऐप के पास भी है)। क्या कोई लेख है जहां इसे विस्तार से दिखाया गया है? सभी उत्तरों और टिप्पणियों की सराहना की जाएगी। धन्यवाद।
कुछ अन्य संदर्भ :
1. पुनर्नवीनीकरण दृश्य के साथ पृष्ठ पर अंक लगाना । (शानदार अवलोकन ...)
2.
RecyclerView
हेडर और पाद
।
(एक जैसा पर उससे अधिक ...)
3.
निचले स्तर पर
ProgressBar
साथ अंतहीन
RecyclerView
।
अलग दृष्टिकोण कुछ प्रगति संकेतक देखने के लिए आइटम में onBindViewHolder और प्रारंभिक स्थान के भीतर एपीआई कॉल शुरू करने के लिए किया जाएगा। कॉल समाप्त होने के बाद, आप दृश्य को अपडेट करते हैं (प्रगति को छिपाएं और प्राप्त डेटा दिखा रहे हैं)। चित्र लोडिंग के लिए पिकासो के साथ उदाहरण के लिए, onBindViewHolder विधि इस तरह दिखाई देगी
@Override
public void onBindViewHolder(final MovieViewHolder holder, final int position) {
final Movie movie = items.get(position);
holder.imageProgress.setVisibility(View.VISIBLE);
Picasso.with(context)
.load(NetworkingUtils.getMovieImageUrl(movie.getPosterPath()))
.into(holder.movieThumbImage, new Callback() {
@Override
public void onSuccess() {
holder.imageProgress.setVisibility(View.GONE);
}
@Override
public void onError() {
}
});
}
जैसा कि मैंने इसे देखा, दो मामले हैं जो सामने आ सकते हैं:
- जहां आप एक संस्करण के साथ लाइट संस्करण में सभी आइटम डाउनलोड करते हैं (जैसे कि एडॉप्टर तुरंत जानता है कि उसे 40 चित्रों से निपटना होगा, लेकिन इसे मांग पर डाउनलोड करें -> मामला जो मैंने पिकासो के साथ पहले दिखाया था)
- जहां आप वास्तविक आलसी लोडिंग के साथ काम कर रहे हैं और आप सर्वर से आपको डेटा का अतिरिक्त हिस्सा देने के लिए कह रहे हैं। इस मामले में, पहली शर्त यह है कि आवश्यक जानकारी के साथ सर्वर से पर्याप्त प्रतिक्रिया हो। उदाहरण उदाहरण {"ऑफसेट": 0, "कुल": 100, "आइटम": [{आइटम}]}
प्रतिक्रिया का मतलब है कि आपको कुल 100 डेटा का पहला हिस्सा मिला है। मेरा दृष्टिकोण कुछ इस तरह होगा:
डेटा का पहला हिस्सा मिलने के बाद देखें (उदाहरण 10) उन्हें एडॉप्टर में जोड़ें।
RecyclerView.Adcape.getItemCount जब तक उपलब्ध वस्तुओं की वर्तमान राशि कुल राशि से कम है (जैसे 10 उपलब्ध; कुल 100), getItemCount विधि में आप items.size () + 1 लौटा देंगे
RecyclerView.Adcape.getItemViewType यदि डेटा की कुल राशि एडॉप्टर में उपलब्ध वस्तुओं की मात्रा से अधिक है और स्थिति = items.size () (यानी आपने getItemCount पद्धति में आइटम को काल्पनिक रूप से जोड़ा गया है), तो उस प्रकार के रूप में जब आप कुछ प्रगति-संकेतक लौटाते हैं । अन्यथा आप सामान्य लेआउट प्रकार वापस करेंगे
RecyclerView.Adcape.onCreateViewHolder जब आपको प्रगति-संकेतक दृश्य प्रकार का उपयोग करने के लिए कहा जाता है, तो आपको केवल अपने प्रस्तोता से वस्तुओं का अतिरिक्त हिस्सा लेने और एडॉप्टर को अपडेट करने के लिए कहना होगा।
तो मूल रूप से, यह वह दृष्टिकोण है जहां आपको सूची से आइटम जोड़ना / हटाना नहीं है और जहां आलसी लोडिंग शुरू हो जाएगी, उस स्थिति पर आपका नियंत्रण है।
यहाँ कोड उदाहरण है:
public class ForecastListAdapter extends RecyclerView.Adapter<ForecastListAdapter.ForecastVH> {
private final Context context;
private List<Forecast> items;
private ILazyLoading lazyLoadingListener;
public static final int VIEW_TYPE_FIRST = 0;
public static final int VIEW_TYPE_REST = 1;
public static final int VIEW_TYPE_PROGRESS = 2;
public static final int totalItemsCount = 14;
public ForecastListAdapter(List<Forecast> items, Context context, ILazyLoading lazyLoadingListener) {
this.items = items;
this.context = context;
this.lazyLoadingListener = lazyLoadingListener;
}
public void addItems(List<Forecast> additionalItems){
this.items.addAll(additionalItems);
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if(totalItemsCount > items.size() && position == items.size()){
return VIEW_TYPE_PROGRESS;
}
switch (position){
case VIEW_TYPE_FIRST:
return VIEW_TYPE_FIRST;
default:
return VIEW_TYPE_REST;
}
}
@Override
public ForecastVH onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType){
case VIEW_TYPE_PROGRESS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.forecast_list_item_progress, parent, false);
if (lazyLoadingListener != null) {
lazyLoadingListener.getAdditionalItems();
}
break;
case VIEW_TYPE_FIRST:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.forecast_list_item_first, parent, false);
break;
default:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.forecast_list_item_rest, parent, false);
break;
}
return new ForecastVH(v);
}
@Override
public void onBindViewHolder(ForecastVH holder, int position) {
if(position < items.size()){
Forecast item = items.get(position);
holder.date.setText(FormattingUtils.formatTimeStamp(item.getDt()));
holder.minTemperature.setText(FormattingUtils.getRoundedTemperature(item.getTemp().getMin()));
holder.maxTemperature.setText(FormattingUtils.getRoundedTemperature(item.getTemp().getMax()));
}
}
@Override
public long getItemId(int position) {
long i = super.getItemId(position);
return i;
}
@Override
public int getItemCount() {
if (items == null) {
return 0;
}
if(items.size() < totalItemsCount){
return items.size() + 1;
}else{
return items.size();
}
}
public class ForecastVH extends RecyclerView.ViewHolder{
@BindView(R.id.forecast_date)TextView date;
@BindView(R.id.min_temperature)TextView minTemperature;
@BindView(R.id.max_temperature) TextView maxTemperature;
public ForecastVH(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public interface ILazyLoading{
public void getAdditionalItems();
}}
शायद यह आपको कुछ ऐसा बनाने के लिए प्रेरित करेगा जो आपकी आवश्यकताओं के अनुरूप होगा
आप इस तरह से recycleView में layout_above टैग का उपयोग कर सकते हैं:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_below="@+id/tv2"
android:layout_width="match_parent"
android:focusable="false"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_above="@+id/pb_pagination"/>
<ProgressBar
android:id="@+id/pb_pagination"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="30dp"
android:layout_height="30dp"
android:indeterminate="true"
android:visibility="gone"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
मुझे एक एडॉप्टर में प्रगति देखने वाले को जोड़ने का विचार पसंद है, लेकिन यह कुछ बदसूरत तर्क हेरफेर को जन्म देता है जो आप चाहते हैं।
व्यू होल्डर अप्रोच आपको
getItemCount()
,
getItemViewType()
,
getItemId(position)
और किसी भी तरह के
getItem(position)
पद्धति के रिटर्न मानों के साथ
getItemCount()
करके आपको किसी भी प्रकार की
getItem(position)
विधि से
getItem(position)
के लिए
getItem(position)
जिसे आप शामिल करना चाहते हैं।
वैकल्पिक तरीका यह है कि लोडिंग शुरू होने और क्रमशः समाप्त होने पर
RecyclerView
नीचे
ProgressBar
को दिखा या छिपाकर
ProgressBar
या
Activity
स्तर पर
ProgressBar
दृश्यता का प्रबंधन किया जाए।
यह
ProgressBar
सीधे व्यू लेआउट में शामिल करके या कस्टम
RecyclerView
ViewGroup
वर्ग में जोड़कर प्राप्त किया जा सकता है।
यह समाधान आमतौर पर कम रखरखाव और कम कीड़े को जन्म देगा।
अद्यतन: जब आप सामग्री को लोड कर रहे हैं, तो मेरे सुझाव को वापस स्क्रॉल करने पर एक समस्या उत्पन्न होती है।
ProgressBar
व्यू लेआउट के निचले हिस्से पर चिपक जाएगा।
यह शायद वह व्यवहार नहीं है जो आप चाहते हैं।
इस कारण से, अपने एडॉप्टर में प्रगति देखने वाले को जोड़ना संभवतः सबसे अच्छा, कार्यात्मक समाधान है।
बस अपने आइटम एक्सेसर विधियों की रक्षा करना न भूलें।
:)
मैंने अपने पुराने प्रोजेक्ट पर इसे लागू किया, मैंने इसे इस प्रकार किया ...
मैंने एक
interface
बनाया
interface
जैसा कि आपके उदाहरण के दोस्तों ने किया था
public interface LoadMoreItems {
void LoadItems();
}
फिर मैं अपने
Adapter
पर एक
addOnScrollListener()
जोड़ा
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
//End of the items
if (onLoadMoreListener != null) {
onLoadMoreListener.LoadItems();
}
loading = true;
}
}
});
onCreateViewHolder()
वह जगह है जहाँ मैंने
ProgressBar
डाला या नहीं।
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progressbar_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
अपनी
MainActivity
पर, जहां मैं अन्य आइटम जोड़ने के लिए
LoadItems()
डाल रहा हूं:
mAdapter.setOnLoadMoreListener(new LoadMoreItems() {
@Override
public void LoadItems() {
DataItemsList.add(null);
mAdapter.notifyItemInserted(DataItemsList.size() - 1);
handler.postDelayed(new Runnable() {
@Override
public void run() {
// remove progress item
DataItemsList.remove(DataItemsList.size() - 1);
mAdapter.notifyItemRemoved(DataItemsList.size());
//add items one by one
//When you've added the items call the setLoaded()
mAdapter.setLoaded();
//if you put all of the items at once call
// mAdapter.notifyDataSetChanged();
}
}, 2000); //time 2 seconds
}
});
अधिक जानकारी के लिए मैंने सिर्फ इस
Github repository
अनुसरण किया (
नोट: यह
AsyncTask
का उपयोग कर
AsyncTask
शायद यह मेरे उत्तर के रूप में उपयोगी है, क्योंकि मैंने इसे मैन्युअल रूप से
API
से डेटा के साथ नहीं किया था, लेकिन साथ ही साथ यह काम
भी
करना चाहिए
) यह पोस्ट मेरे लिए
endless-recyclerview-with-progress-bar
सहायक थी
endless-recyclerview-with-progress-bar
इसके अलावा, मुझे नहीं पता कि आपने इसका नाम रखा है, लेकिन मुझे यह पोस्ट infinite_scrolling_recyclerview भी मिली, शायद यह भी आपकी मदद कर सके।
यदि यह वह नहीं है जो आप ढूंढ रहे हैं, तो मुझे बताएं और मुझे बताएं कि इस कोड में क्या गलत है और मैं इसे आपकी तरह संशोधित करने का प्रयास करूंगा।
आशा है ये मदद करेगा।
संपादित करें
चूँकि आप किसी आइटम को हटाना नहीं चाहते हैं ... मैंने पाया कि मैं एक आदमी का अनुमान लगाता हूँ जो इस पद पर केवल
footer
निकालता है:
diseño-android-endless-recyclerview
।
यह
ListView
लिए है, लेकिन मुझे पता है कि आप इसे
RecyclerView
अनुकूलित कर सकते हैं वह किसी भी आइटम को हटा नहीं रहा है जिसे वह अभी देख रहा है
Visible/Invisible
है
ProgressBar
एक नज़र डालें:
detecting-end-of-listview
इस पर भी एक नज़र डालें: यह प्रश्न
android-implementing-progressbar-and-loading-for-endless-list-like-android
यहाँ एक नकली वस्तु (कोटलिन में लेकिन सरल) को जोड़े बिना मेरा काम है:
अपने एडॉप्टर में जोड़ें:
private var isLoading = false
private val VIEWTYPE_FORECAST = 1
private val VIEWTYPE_PROGRESS = 2
override fun getItemCount(): Int {
if (isLoading)
return items.size + 1
else
return items.size
}
override fun getItemViewType(position: Int): Int {
if (position == items.size - 1 && isLoading)
return VIEWTYPE_PROGRESS
else
return VIEWTYPE_FORECAST
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEWTYPE_FORECAST)
return ForecastHolder(LayoutInflater.from(context).inflate(R.layout.item_forecast, parent, false))
else
return ProgressHolder(LayoutInflater.from(context).inflate(R.layout.item_progress, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
if (holder is ForecastHolder) {
//init your item
}
}
public fun showProgress() {
isLoading = true
}
public fun hideProgress() {
isLoading = false
}
अब आप API कॉल से पहले आसानी से
showProgress()
कॉल कर सकते हैं।
और एपीआई कॉल करने के बाद
hideProgress()
।
यहाँ SIMPLER और स्वच्छ APPROACH है।
इस कोडपथ गाइड से अंतहीन स्क्रॉलिंग को लागू करें और फिर निम्नलिखित चरणों का पालन करें।
1. RecyclerView के तहत प्रगति बार जोड़ें।
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_movie_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="50dp"
android:clipToPadding="false"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.RecyclerView>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
यहाँ Android: paddingBottom = "50dp" और android: clipToPadding = "false" बहुत महत्वपूर्ण हैं।
2. प्रगति पट्टी का संदर्भ लें।
progressBar = findViewById(R.id.progressBar);
3. प्रगति पट्टी को दिखाने और छिपाने के लिए तरीकों को परिभाषित करें।
void showProgressView() {
progressBar.setVisibility(View.VISIBLE);
}
void hideProgressView() {
progressBar.setVisibility(View.INVISIBLE);
}