Android में ListView/RecyclerView रीसाइक्लिंग मैकेनिज्म समझाया गया
यह ट्यूटोरियल बताता है कि Android में व्यू रीसाइक्लिंग कैसे काम करता है - ListView के कन्वर्टव्यू पैटर्न से लेकर RecyclerView के अनिवार्य ViewHolder पैटर्न तक।
रीसाइक्लिंग क्यों जरूरी है?
बिना रीसाइक्लिंग के, 1000 आइटम्स की लिस्ट के लिए 1000 व्यू ऑब्जेक्ट्स बनेंगे - मेमोरी खत्म हो जाएगी और स्क्रॉलिंग लैगी होगी। रीसाइक्लिंग से केवल विजिबल आइटम्स (+ बफर) के व्यू बनते हैं।
ListView का कन्वर्टव्यू पैटर्न (लीगेसी)
// ListView एडाप्टर में getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
// नया व्यू इन्फ्लेट करें - केवल पहली बार
convertView = LayoutInflater.from(context).inflate(R.layout.item, parent, false);
holder = new ViewHolder();
holder.textView = convertView.findViewById(R.id.textView);
convertView.setTag(holder);
} else {
// मौजूदा व्यू पुनः उपयोग करें
holder = (ViewHolder) convertView.getTag();
}
// डेटा बाइंड करें
holder.textView.setText(getItem(position));
return convertView;
}
static class ViewHolder {
TextView textView;
}
RecyclerView का अनिवार्य ViewHolder पैटर्न
RecyclerView में रीसाइक्लिंग अनिवार्य है - कोई कन्वर्टव्यू पैरामीटर नहीं:
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private List<String> cities;
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// केवल नए ViewHolder बनाएं जब पूल खाली हो
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_row, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// केवल डेटा बाइंड करें - व्यू पहले से बना हुआ है
holder.tvCountry.setText(cities.get(position));
}
@Override
public int getItemCount() {
return cities.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvCountry;
ViewHolder(View view) {
super(view);
tvCountry = view.findViewById(R.id.tv_country);
}
}
}
रीसाइक्लिंग प्रक्रिया विजुअलाइज़ेशन
स्क्रॉल डाउन:
┌─────────────────────────────────────┐
│ Item 0 (विजिबल) │ → स्क्रॉल आउट → रीसाइक्लर पूल में जाता है
│ Item 1 (विजिबल) │
│ Item 2 (विजिबल) │
│ Item 3 (विजिबल) │
│ Item 4 (आंशिक) │
└─────────────────────────────────────┘
↓ यूजर स्क्रॉल करता है ↓
┌─────────────────────────────────────┐
│ Item 1 (विजिबल) │
│ Item 2 (विजिबल) │
│ Item 3 (विजिबल) │
│ Item 4 (विजिबल) │
│ Item 5 (नया - पूल से रीसाइक्ल्ड व्यू) │ ← Item 0 का व्यू पुनः उपयोग हुआ
└─────────────────────────────────────┘
चरण 1-6: पूरा उदाहरण (RecyclerView के साथ)
कार्ड रो लेआउट res/layout/card_row.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<TextView
android:id="@+id/tv_country"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="10dp"
android:textSize="18sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
MainActivity src/MainActivity.java:
package com.app.sample;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ArrayList<String> cities;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
RecyclerView recyclerView = findViewById(R.id.card_recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
cities = new ArrayList<>();
cities.add("मुंबई");
cities.add("चेन्नई");
cities.add("कोच्चि");
cities.add("बेंगलुरु");
cities.add("हैदराबाद");
cities.add("अमरावती");
cities.add("गोवा");
cities.add("तिरुवनंतपुरम");
cities.add("कोयंबटूर");
cities.add("वेल्लोर");
cities.add("सेलम");
cities.add("भुवनेश्वर");
cities.add("कटक");
cities.add("मैसूर");
DataAdapter adapter = new DataAdapter(cities);
recyclerView.setAdapter(adapter);
// आइटम क्लिक हैंडलिंग
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
GestureDetector gestureDetector = new GestureDetector(getApplicationContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) { return true; }
});
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && gestureDetector.onTouchEvent(e)) {
int position = rv.getChildAdapterPosition(child);
Toast.makeText(getApplicationContext(),
"चुना गया: " + cities.get(position), Toast.LENGTH_SHORT).show();
}
return false;
}
@Override public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
@Override public void onRequestDisallowInterceptTouchEvent(boolean disallow) {}
});
}
}
DataAdapter src/DataAdapter.java:
package com.app.sample;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private ArrayList<String> cities;
public DataAdapter(ArrayList<String> cities) {
this.cities = cities;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_row, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.tvCountry.setText(cities.get(position));
}
@Override
public int getItemCount() {
return cities.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvCountry;
ViewHolder(View view) {
super(view);
tvCountry = view.findViewById(R.id.tv_country);
}
}
}
आधुनिक बेहतरी: ListAdapter + DiffUtil
public class ModernAdapter extends ListAdapter<String, ModernAdapter.ViewHolder> {
public ModernAdapter() {
super(new DiffUtil.ItemCallback<String>() {
@Override
public boolean areItemsTheSame(@NonNull String old, @NonNull String newItem) {
return old.equals(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull String old, @NonNull String newItem) {
return old.equals(newItem);
}
});
}
// ... ViewHolder और बाइंडिंग
}
रीसाइक्लिंग के लिए बेस्ट प्रैक्टिसेज
- onCreateViewHolder में न्यूनतम काम करें - केवल इन्फ्लेट और findViewById
- onBindViewHolder में केवल डेटा सेट करें - लेआउट पैरामीटर्स न बदलें
- setHasFixedSize(true) सेट करें अगर आइटम साइज फिक्स्ड है
- पेलोड्स के साथ notifyItemChanged() उपयोग करें आंशिक अपडेट के लिए
- ViewHolder में महंगे ऑब्जेक्ट्स कैश करें - इमेज लोडर, फॉर्मेटर्स
- RecyclerView.RecycledViewPool शेयर करें एक्टिविटीज के बीच
आम गलतियां
| गलती | समस्या | समाधान |
|---|---|---|
| onBindViewHolder में findViewById | परफॉरमेंस हिट | ViewHolder में कैश करें |
| पोजीशन के आधार पर लेआउट पैरामीटर्स बदलना | रीसाइक्लिंग बग्स | लेआउट XML में हैंडल करें |
| notifyDataSetChanged() ओवरयूज | नो एनिमेशन, फुल रीबाइंड | स्पेसिफिक notifyItem* मेथड्स |
| एसिंक ऑपरेशन में पोजीशन उपयोग करना | गलत डेटा बाइंडिंग | getAdapterPosition() या होल्डर टैग उपयोग करें |