RecyclerView के साथ GridLayoutManager - पूर्ण गाइड
GridLayoutManager आइटम को एक ग्रिड में व्यवस्थित करता है (जैसे फोटो गैलरी, उत्पाद सूचियां)। यह स्पैन काउंट, स्पैन आकार लुकअप, और RecyclerView एनिमेशन का समर्थन करता है।
निर्भरताएं
dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "com.google.android.material:material:1.11.0"
}
बुनियादी सेटअप
RecyclerView rv = findViewById(R.id.recyclerView); int spanCount = 2; // कॉलम GridLayoutManager glm = new GridLayoutManager(this, spanCount); rv.setLayoutManager(glm); rv.setAdapter(new MyAdapter(dataList));
उन्नत: डायनामिक स्पैन आकार (हेडर, फुल-विड्थ आइटम)
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
// स्थिति 0 = हेडर (पूर्ण चौड़ाई)
return (position == 0) ? spanCount : 1;
}
});
आइटम डेकोरेशन - ग्रिड स्पेसिंग
public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
private final boolean includeEdge;
public GridSpacingDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) outRect.top = spacing;
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) outRect.top = spacing;
outRect.bottom = 0;
}
}
}
// उपयोग:
rv.addItemDecoration(new GridSpacingDecoration(2, 16, true));
पूर्ण उदाहरण: अनुकूली कॉलम (उपलब्ध चौड़ाई के आधार पर)
public class AdaptiveGridManager extends GridLayoutManager {
private final int minItemWidth;
public AdaptiveGridManager(Context context, int minItemWidth) {
super(context, 1); // अस्थायी
this.minItemWidth = minItemWidth;
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getWidth() > 0 && getSpanCount() != calculateSpanCount()) {
setSpanCount(calculateSpanCount());
requestLayout();
}
super.onLayoutChildren(recycler, state);
}
private int calculateSpanCount() {
return Math.max(1, getWidth() / minItemWidth);
}
}
// उपयोग: new AdaptiveGridManager(this, 180) // न्यूनतम 180dp प्रति कॉलम
एडॉप्टर पैटर्न (ListAdapter + DiffUtil)
public class GridAdapter extends ListAdapter- { public GridAdapter() { super(DIFF); } private static final DiffUtil.ItemCallback
- DIFF = new DiffUtil.ItemCallback
- () { @Override public boolean areItemsTheSame(@NonNull Item o, @NonNull Item n) { return o.id == n.id; } @Override public boolean areContentsTheSame(@NonNull Item o, @NonNull Item n) { return o.equals(n); } }; @NonNull @Override public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid, parent, false); return new VH(v); } @Override public void onBindViewHolder(@NonNull VH h, int pos) { h.bind(getItem(pos)); } static class VH extends RecyclerView.ViewHolder { ImageView img; TextView title; VH(View v) { super(v); img = v.findViewById(R.id.iv); title = v.findViewById(R.id.tv); } void bind(Item i) { title.setText(i.title); img.setImageResource(i.imageRes); } } }
XML लेआउट (item_grid.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:src="@drawable/placeholder" />
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:textSize="16sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
सर्वोत्तम अभ्यास
- ListAdapter का उपयोग करें स्वचालित एनिमेटेड अपडेट के लिए।
- ItemDecoration के साथ स्पेसिंग सेट करें, आइटम लेआउट मार्जिन नहीं (स्क्रॉल जैंक से बचाता है)।
- अनुकूली स्पैन के लिए
SpanSizeLookupया कस्टम GridLayoutManager का उपयोग करें। - बड़ी इमेज के लिए Glide/Coil का उपयोग करें जिसमें
override(width, height)हो ताकि OOM से बचा जा सके। - नेस्टेड स्क्रॉलिंग अक्षम करें यदि ग्रिड स्क्रॉलिंग कंटेनर के अंदर है:
rv.setNestedScrollingEnabled(false)।
मूल उदाहरण की समीक्षा
मूल कोड लिगेसी सपोर्ट लाइब्रेरी, हार्डकोडेड 2-कॉलम ग्रिड, मैन्युअल notifyDataSetChanged, और एक अधूरे एडॉप्टर का उपयोग करता था। ऊपर दिया गया समाधान आधुनिक androidx, अनुकूली कॉलम, उचित स्पेसिंग, और DiffUtil-आधारित एडॉप्टर का उपयोग करता है।