आप GridLayoutManager का उपयोग करके RecyclerView के साथ कॉलम रिक्ति कैसे सेट करते हैं? मेरे लेआउट के अंदर मार्जिन / पैडिंग सेट करने का कोई प्रभाव नहीं है।
आप GridLayoutManager का उपयोग करके RecyclerView के साथ कॉलम रिक्ति कैसे सेट करते हैं? मेरे लेआउट के अंदर मार्जिन / पैडिंग सेट करने का कोई प्रभाव नहीं है।
जवाबों:
RecyclerViews ItemDecoration की अवधारणा का समर्थन करता है : प्रत्येक तत्व के आसपास विशेष ऑफसेट और ड्राइंग। जैसा कि इस उत्तर में देखा गया है , आप उपयोग कर सकते हैं
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.bottom = space;
// Add top margin only for the first item to avoid double space between items
if (parent.getChildLayoutPosition(view) == 0) {
outRect.top = space;
} else {
outRect.top = 0;
}
}
}
फिर इसके माध्यम से जोड़ें
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
int spacingInPixels = getResources().getDimensionPixelSize(R.dimen.spacing);
mRecyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels));
RecyclerView
(और उपयोग clipToPadding="false"
) के हिस्से के रूप में शीर्ष और नीचे की गद्दी का उपयोग करते हैं , तो आप चीजों को थोड़ा सा पुनर्गठन कर सकते हैं। यदि आप हालांकि नहीं करते हैं, तो आप पिछली बार होने वाले चेक को स्थानांतरित कर देंगे (जैसा कि आप अभी भी अंतिम आइटम पर नीचे पैडिंग चाहते हैं)।
GridLayoutManager
। उत्तर पर बहु स्तंभ / पंक्ति लेआउट काम नहीं करेगा
निम्नलिखित कोड अच्छी तरह से काम करता है, और प्रत्येक कॉलम की चौड़ाई समान है:
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
int spanCount = 3; // 3 columns
int spacing = 50; // 50px
boolean includeEdge = false;
recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge));
int spanCount = 3; // 3 columns
int spacing = 50; // 50px
boolean includeEdge = true;
recyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge));
निम्नलिखित चरण-दर-चरण सरल समाधान है यदि आप वस्तुओं और समान आइटम आकारों के बराबर रिक्ति चाहते हैं।
public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
private int mItemOffset;
public ItemOffsetDecoration(int itemOffset) {
mItemOffset = itemOffset;
}
public ItemOffsetDecoration(@NonNull Context context, @DimenRes int itemOffsetId) {
this(context.getResources().getDimensionPixelSize(itemOffsetId));
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(mItemOffset, mItemOffset, mItemOffset, mItemOffset);
}
}
अपने स्रोत कोड में, ItemOffsetDecoration
अपने RecyclerView.
आइटम ऑफसेट मूल्य में उस वास्तविक मूल्य का आधा आकार होना चाहिए जिसे आप आइटम के बीच के स्थान के रूप में जोड़ना चाहते हैं।
mRecyclerView.setLayoutManager(new GridLayoutManager(context, NUM_COLUMNS);
ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(context, R.dimen.item_offset);
mRecyclerView.addItemDecoration(itemDecoration);
इसके अलावा, आइटम ऑफसेट मूल्य को इसके लिए पैडिंग के रूप में सेट RecyclerView
करें, और निर्दिष्ट करें android:clipToPadding=false
।
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="@dimen/item_offset"/>
इसे इस्तेमाल करे। यह चारों ओर समान दूरी का ध्यान रखेगा। सूची, ग्रिड और चौंका देने वाला दोनों के साथ काम करता है।
अपडेट किए गए कोड को स्पैन, ओरिएंटेशन, आदि के अधिकांश कोने के मामलों को संभालना चाहिए। ध्यान दें कि यदि सेटस्स्पैनलाइज़ मैनपावर () का उपयोग ग्रिडलाइयूटमैनेजर के साथ किया जाता है, तो सेटस्पैनइंडेक्सचेचेइन्फेक्शंस () को प्रदर्शन कारणों से सेट किया जाता है।
ध्यान दें, ऐसा लगता है कि StaggeredGrid के साथ, एक बग प्रतीत होता है, जहां बच्चों का सूचकांक निराला और मुश्किल हो जाता है इसलिए नीचे दिए गए कोड StaggeredGridLayoutManager के साथ बहुत अच्छी तरह से काम नहीं कर सकते हैं।
public class ListSpacingDecoration extends RecyclerView.ItemDecoration {
private static final int VERTICAL = OrientationHelper.VERTICAL;
private int orientation = -1;
private int spanCount = -1;
private int spacing;
private int halfSpacing;
public ListSpacingDecoration(Context context, @DimenRes int spacingDimen) {
spacing = context.getResources().getDimensionPixelSize(spacingDimen);
halfSpacing = spacing / 2;
}
public ListSpacingDecoration(int spacingPx) {
spacing = spacingPx;
halfSpacing = spacing / 2;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (orientation == -1) {
orientation = getOrientation(parent);
}
if (spanCount == -1) {
spanCount = getTotalSpan(parent);
}
int childCount = parent.getLayoutManager().getItemCount();
int childIndex = parent.getChildAdapterPosition(view);
int itemSpanSize = getItemSpanSize(parent, childIndex);
int spanIndex = getItemSpanIndex(parent, childIndex);
/* INVALID SPAN */
if (spanCount < 1) return;
setSpacings(outRect, parent, childCount, childIndex, itemSpanSize, spanIndex);
}
protected void setSpacings(Rect outRect, RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) {
outRect.top = halfSpacing;
outRect.bottom = halfSpacing;
outRect.left = halfSpacing;
outRect.right = halfSpacing;
if (isTopEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) {
outRect.top = spacing;
}
if (isLeftEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) {
outRect.left = spacing;
}
if (isRightEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) {
outRect.right = spacing;
}
if (isBottomEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) {
outRect.bottom = spacing;
}
}
@SuppressWarnings("all")
protected int getTotalSpan(RecyclerView parent) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getSpanCount();
} else if (mgr instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager) mgr).getSpanCount();
} else if (mgr instanceof LinearLayoutManager) {
return 1;
}
return -1;
}
@SuppressWarnings("all")
protected int getItemSpanSize(RecyclerView parent, int childIndex) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex);
} else if (mgr instanceof StaggeredGridLayoutManager) {
return 1;
} else if (mgr instanceof LinearLayoutManager) {
return 1;
}
return -1;
}
@SuppressWarnings("all")
protected int getItemSpanIndex(RecyclerView parent, int childIndex) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount);
} else if (mgr instanceof StaggeredGridLayoutManager) {
return childIndex % spanCount;
} else if (mgr instanceof LinearLayoutManager) {
return 0;
}
return -1;
}
@SuppressWarnings("all")
protected int getOrientation(RecyclerView parent) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof LinearLayoutManager) {
return ((LinearLayoutManager) mgr).getOrientation();
} else if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getOrientation();
} else if (mgr instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager) mgr).getOrientation();
}
return VERTICAL;
}
protected boolean isLeftEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) {
if (orientation == VERTICAL) {
return spanIndex == 0;
} else {
return (childIndex == 0) || isFirstItemEdgeValid((childIndex < spanCount), parent, childIndex);
}
}
protected boolean isRightEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) {
if (orientation == VERTICAL) {
return (spanIndex + itemSpanSize) == spanCount;
} else {
return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex);
}
}
protected boolean isTopEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) {
if (orientation == VERTICAL) {
return (childIndex == 0) || isFirstItemEdgeValid((childIndex < spanCount), parent, childIndex);
} else {
return spanIndex == 0;
}
}
protected boolean isBottomEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) {
if (orientation == VERTICAL) {
return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex);
} else {
return (spanIndex + itemSpanSize) == spanCount;
}
}
protected boolean isFirstItemEdgeValid(boolean isOneOfFirstItems, RecyclerView parent, int childIndex) {
int totalSpanArea = 0;
if (isOneOfFirstItems) {
for (int i = childIndex; i >= 0; i--) {
totalSpanArea = totalSpanArea + getItemSpanSize(parent, i);
}
}
return isOneOfFirstItems && totalSpanArea <= spanCount;
}
protected boolean isLastItemEdgeValid(boolean isOneOfLastItems, RecyclerView parent, int childCount, int childIndex, int spanIndex) {
int totalSpanRemaining = 0;
if (isOneOfLastItems) {
for (int i = childIndex; i < childCount; i++) {
totalSpanRemaining = totalSpanRemaining + getItemSpanSize(parent, i);
}
}
return isOneOfLastItems && (totalSpanRemaining <= spanCount - spanIndex);
}
}
आशा करता हूँ की ये काम करेगा।
निम्न कोड StaggeredGridLayoutManager, GridLayoutManager, और LinearLayoutManager को संभालेगा।
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int halfSpace;
public SpacesItemDecoration(int space) {
this.halfSpace = space / 2;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getPaddingLeft() != halfSpace) {
parent.setPadding(halfSpace, halfSpace, halfSpace, halfSpace);
parent.setClipToPadding(false);
}
outRect.top = halfSpace;
outRect.bottom = halfSpace;
outRect.left = halfSpace;
outRect.right = halfSpace;
}
}
फिर इसका उपयोग करें
mRecyclerView.addItemDecoration(new SpacesItemDecoration(mMargin));
SpaceItemDecoration
वास्तव में माता-पिता (recycler देखें) करने के लिए गद्दी कहते हैं।
halfSpace
पैडिंग दिखाई गई (दाईं ओर) जब मैंने xml में माता-पिता को पैडिंग सेट नहीं किया था
यहाँ एक समाधान है जिसे "स्पैनकाउंट" (कॉलमों की संख्या) की आवश्यकता नहीं है, मैं इसका उपयोग करता हूं क्योंकि मैं GridAutofitLayoutManager का उपयोग करता हूं (आवश्यक सेल आकार के अनुसार कॉलम की संख्या की गणना करता है)
(सावधान रहें कि यह केवल GridLayoutManager पर काम करेगा )
public class GridSpacesItemDecoration extends RecyclerView.ItemDecoration {
private final boolean includeEdge;
private int spacing;
public GridSpacesItemDecoration(int spacing, boolean includeEdge) {
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager)parent.getLayoutManager();
int spanCount = layoutManager.getSpanCount();
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
}
यहाँ है GridAutofitLayoutManager किसी को भी दिलचस्पी है:
public class GridAutofitLayoutManager extends GridLayoutManager {
private int mColumnWidth;
private boolean mColumnWidthChanged = true;
public GridAutofitLayoutManager(Context context, int columnWidth)
{
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
public GridAutofitLayoutManager(Context context,int unit, int columnWidth)
{
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
int pixColumnWidth = (int) TypedValue.applyDimension(unit, columnWidth, context.getResources().getDisplayMetrics());
setColumnWidth(checkedColumnWidth(context, pixColumnWidth));
}
public GridAutofitLayoutManager(Context context, int columnWidth, int orientation, boolean reverseLayout)
{
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1, orientation, reverseLayout);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
private int checkedColumnWidth(Context context, int columnWidth)
{
if (columnWidth <= 0)
{
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
return columnWidth;
}
public void setColumnWidth(int newColumnWidth)
{
if (newColumnWidth > 0 && newColumnWidth != mColumnWidth)
{
mColumnWidth = newColumnWidth;
mColumnWidthChanged = true;
}
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
{
int width = getWidth();
int height = getHeight();
if (mColumnWidthChanged && mColumnWidth > 0 && width > 0 && height > 0)
{
int totalSpace;
if (getOrientation() == VERTICAL)
{
totalSpace = width - getPaddingRight() - getPaddingLeft();
}
else
{
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
int spanCount = Math.max(1, totalSpace / mColumnWidth);
setSpanCount(spanCount);
mColumnWidthChanged = false;
}
super.onLayoutChildren(recycler, state);
}
}
आखिरकार:
mDevicePhotosView.setLayoutManager(new GridAutofitLayoutManager(getContext(), getResources().getDimensionPixelSize(R.dimen.item_size)));
mDevicePhotosView.addItemDecoration(new GridSpacesItemDecoration(Util.dpToPx(getContext(), 2),true));
layoutManager.getPosition(view)
उसके बाद जाँच करें कि क्या स्थिति शून्य है जो आपका हेडर होगा .. इसके अलावा, इस तरह से आप किसी भी स्थिति में किसी अन्य हेडर को जोड़ सकेंगे जो आप चाहते हैं :)
केवल एक आसान समाधान है, जिसे आप याद कर सकते हैं और जहां भी आवश्यकता हो, कार्यान्वित कर सकते हैं। कोई कीड़े नहीं, कोई पागल गणना नहीं। कार्ड / आइटम लेआउट के लिए मार्जिन रखें और RecyclerView को पैडिंग के समान आकार दें:
item_layout.xml
<CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:margin="10dp">
activity_layout.xml
<RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"/>
यदि आप सभी उपकरणों में अपने आइटम का आकार FIXED करना चाहते हैं RecyclerView
। आप ऐसा कर सकते हैं
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int mSpanCount;
private float mItemSize;
public GridSpacingItemDecoration(int spanCount, int itemSize) {
this.mSpanCount = spanCount;
mItemSize = itemSize;
}
@Override
public void getItemOffsets(final Rect outRect, final View view, RecyclerView parent,
RecyclerView.State state) {
final int position = parent.getChildLayoutPosition(view);
final int column = position % mSpanCount;
final int parentWidth = parent.getWidth();
int spacing = (int) (parentWidth - (mItemSize * mSpanCount)) / (mSpanCount + 1);
outRect.left = spacing - column * spacing / mSpanCount;
outRect.right = (column + 1) * spacing / mSpanCount;
if (position < mSpanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
}
}
recyclerview_item.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/recycler_view_item_width"
...
>
...
</LinearLayout>
dimens.xml
<dimen name="recycler_view_item_width">60dp</dimen>
गतिविधि
int numberOfColumns = 3;
mRecyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns));
mRecyclerView.setAdapter(...);
mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(3,
getResources().getDimensionPixelSize(R.dimen.recycler_view_item_width)));
चयनित उत्तर लगभग सही है, लेकिन अंतरिक्ष के आधार पर, आइटम की चौड़ाई बराबर नहीं हो सकती है। (मेरे मामले में यह महत्वपूर्ण था)। इसलिए मैंने इस कोड को समाप्त कर दिया है जो अंतरिक्ष को थोड़ा बढ़ाता है, इसलिए आइटम सभी समान चौड़ाई वाले हैं।
class GridSpacingItemDecoration(private val columnCount: Int, @Px preferredSpace: Int, private val includeEdge: Boolean): RecyclerView.ItemDecoration() {
/**
* In this algorithm space should divide by 3 without remnant or width of items can have a difference
* and we want them to be exactly the same
*/
private val space = if (preferredSpace % 3 == 0) preferredSpace else (preferredSpace + (3 - preferredSpace % 3))
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State?) {
val position = parent.getChildAdapterPosition(view)
if (includeEdge) {
when {
position % columnCount == 0 -> {
outRect.left = space
outRect.right = space / 3
}
position % columnCount == columnCount - 1 -> {
outRect.right = space
outRect.left = space / 3
}
else -> {
outRect.left = space * 2 / 3
outRect.right = space * 2 / 3
}
}
if (position < columnCount) {
outRect.top = space
}
outRect.bottom = space
} else {
when {
position % columnCount == 0 -> outRect.right = space * 2 / 3
position % columnCount == columnCount - 1 -> outRect.left = space * 2 / 3
else -> {
outRect.left = space / 3
outRect.right = space / 3
}
}
if (position >= columnCount) {
outRect.top = space
}
}
}
}
columnCount == 1 -> { outRect.left = space outRect.right = space }
@Edwardaa ने कोड प्रदान किया और मैंने RTL का समर्थन करना सही समझा:
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
private int headerNum;
private boolean isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge, int headerNum) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
this.headerNum = headerNum;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view) - headerNum; // item position
if (position >= 0) {
int column = position % spanCount; // item column
if(isRtl) {
column = spanCount - 1 - column;
}
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
} else {
outRect.left = 0;
outRect.right = 0;
outRect.top = 0;
outRect.bottom = 0;
}
}
}
ऊपर दिए गए जवाबों ने GridLayoutManager और LinearLayoutManager के मार्जिन को सेट करने के तरीकों को स्पष्ट किया है।
लेकिन StaggeredGridLayoutManager के लिए, Pradad Sakhizada का जवाब कहता है: "यह StaggeredGridLayoutManager के साथ बहुत अच्छा काम नहीं कर सकता है"। यह IndexOfSpan के बारे में समस्या होनी चाहिए।
आप इसे इस तरह से प्राप्त कर सकते हैं:
private static class MyItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int index = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
}
}
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
int column = params.getSpanIndex();
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
एडवर्डा के उत्तर से थोड़ा अलग, अंतर यह है कि कॉलम कैसे निर्धारित किया जाता है, क्योंकि विभिन्न ऊंचाइयों वाले आइटम जैसे मामलों में, कॉलम को केवल% द्वारा निर्धारित नहीं किया जा सकता है spanCount
class VerticalGridSpacingDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: State
) {
val layoutManager = parent.layoutManager as? GridLayoutManager
if (layoutManager == null || layoutManager.orientation != VERTICAL) {
return super.getItemOffsets(outRect, view, parent, state)
}
val spanCount = layoutManager.spanCount
val position = parent.getChildAdapterPosition(view)
val column = position % spanCount
with(outRect) {
left = if (column == 0) 0 else spacing / 2
right = if (column == spanCount.dec()) 0 else spacing / 2
top = if (position < spanCount) 0 else spacing
}
}
}
यहाँ मेरा संशोधन है SpacesItemDecoration
जो शीर्ष, नीचे, बाएँ और दाएँ समान रूप से numOfColums और स्थान ले सकता है ।
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
private int mNumCol;
public SpacesItemDecoration(int space, int numCol) {
this.space = space;
this.mNumCol=numCol;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
//outRect.right = space;
outRect.bottom = space;
//outRect.left = space;
//Log.d("ttt", "item position" + parent.getChildLayoutPosition(view));
int position=parent.getChildLayoutPosition(view);
if(mNumCol<=2) {
if (position == 0) {
outRect.left = space;
outRect.right = space / 2;
} else {
if ((position % mNumCol) != 0) {
outRect.left = space / 2;
outRect.right = space;
} else {
outRect.left = space;
outRect.right = space / 2;
}
}
}else{
if (position == 0) {
outRect.left = space;
outRect.right = space / 2;
} else {
if ((position % mNumCol) == 0) {
outRect.left = space;
outRect.right = space/2;
} else if((position % mNumCol) == (mNumCol-1)){
outRect.left = space/2;
outRect.right = space;
}else{
outRect.left=space/2;
outRect.right=space/2;
}
}
}
if(position<mNumCol){
outRect.top=space;
}else{
outRect.top=0;
}
// Add top margin only for the first item to avoid double space between items
/*
if (parent.getChildLayoutPosition(view) == 0 ) {
} else {
outRect.top = 0;
}*/
}
}
और अपने तर्क पर नीचे दिए गए कोड का उपयोग करें।
recyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels, numCol));
केवल XML का उपयोग करने वाली इस समस्या के लिए एक बहुत ही सरल और अभी तक लचीला समाधान है जो प्रत्येक LayoutManager पर काम करता है।
मान लें कि आप X (उदाहरण के लिए 8dp) के बराबर रिक्ति चाहते हैं।
दूसरे लेआउट में अपने कार्ड व्यू आइटम को लपेटें
बाहरी लेआउट को X / 2 (4dp) की पैडिंग दें
बाहरी लेआउट पृष्ठभूमि को पारदर्शी बनाएं
...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/transparent"
android:padding="4dip">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.CardView>
</LinearLayout>
...
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp" />
और बस। आपके पास X (8dp) की सही जगह है।
उन लोगों के लिए, जिन्हें स्ट्रैग्ड लयआउट मैनजर की समस्या है (जैसे https://imgur.com/XVutH5u )
recyclerView के तरीके:
getChildAdapterPosition(view)
getChildLayoutPosition(view)
कभी-कभी इंडेक्स के रूप में -1 लौटाते हैं ताकि हम आइटमडेकॉर को सेट करने में परेशानी का सामना कर सकें। मेरा समाधान है कि मद विच्छेद की विधि को हटा दिया जाए:
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
नौसिखिया के बजाय:
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
इस तरह:
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
TheAdapter.VH vh = (TheAdapter.VH) recyclerView.findViewHolderForAdapterPosition(itemPosition);
View itemView = vh.itemView; //itemView is the base view of viewHolder
//or instead of the 2 lines above maybe it's possible to use View itemView = layoutManager.findViewByPosition(itemPosition) ... NOT TESTED
StaggeredGridLayoutManager.LayoutParams itemLayoutParams = (StaggeredGridLayoutManager.LayoutParams) itemView.getLayoutParams();
int spanIndex = itemLayoutParams.getSpanIndex();
if (spanIndex == 0)
...
else
...
}
});
मेरे लिए अब तक काम लगता है :)
यह RecyclerView
हेडर के साथ भी काम करेगा ।
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
private int headerNum;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge, int headerNum) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
this.headerNum = headerNum;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view) - headerNum; // item position
if (position >= 0) {
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
} else {
outRect.left = 0;
outRect.right = 0;
outRect.top = 0;
outRect.bottom = 0;
}
}
}
}
yqritc के जवाब ने मेरे लिए पूरी तरह से काम किया। मैं कोटलिन का उपयोग कर रहा था लेकिन यहाँ उसी के बराबर है।
class ItemOffsetDecoration : RecyclerView.ItemDecoration {
// amount to add to padding
private val _itemOffset: Int
constructor(itemOffset: Int) {
_itemOffset = itemOffset
}
constructor(@NonNull context: Context, @DimenRes itemOffsetId: Int){
_itemOffset = context.resources.getDimensionPixelSize(itemOffsetId)
}
/**
* Applies padding to all sides of the [Rect], which is the container for the view
*/
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.set(_itemOffset, _itemOffset, _itemOffset, _itemOffset)
}
}
बाकी हर कोई एक जैसा है।
के लिए StaggeredGridLayoutManager उपयोगकर्ताओं, सावधान रहना, यहाँ सहित अधिकांश जवाब के बहुत सारे कोड के नीचे के साथ एक गणना आइटम स्तंभ मतदान:
int column = position % spanCount
जो मानता है कि 1 / 3rd / 5th / .. आइटम हमेशा बाईं ओर स्थित होते हैं और 2nd / 4th / 6th / .. आइटम हमेशा दाईं ओर स्थित होते हैं। क्या यह धारणा हमेशा सच है? नहीं।
मान लीजिए कि आपका पहला आइटम 100dp उच्च है और दूसरा केवल 50dp है, अनुमान करें कि आपका 3 आइटम कहाँ स्थित है, बाएँ या दाएँ?
मैंने उसे अपने RecyclerView के लिए GridLayoutManager और HeaderView के साथ ऐसा करना समाप्त कर दिया ।
नीचे दिए गए कोड में मैंने प्रत्येक आइटम के बीच एक 4dp स्थान (प्रत्येक एकल वस्तु के आसपास 2dp और पूरे recyclerview के आसपास 2dp पैडिंग) निर्धारित किया है।
layout.xml
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="2dp" />
टुकड़ा / गतिविधि
GridLayoutManager manager = new GridLayoutManager(getContext(), 3);
recyclerView.setLayoutManager(manager);
int spacingInPixels = Utils.dpToPx(2);
recyclerView.addItemDecoration(new SpacesItemDecoration(spacingInPixels));
SpaceItemDecoration.java
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int mSpacing;
public SpacesItemDecoration(int spacing) {
mSpacing = spacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView recyclerView, RecyclerView.State state) {
outRect.left = mSpacing;
outRect.top = mSpacing;
outRect.right = mSpacing;
outRect.bottom = mSpacing;
}
}
Utils.java
public static int dpToPx(final float dp) {
return Math.round(dp * (Resources.getSystem().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
करने के लिए बनाया https://stackoverflow.com/a/29905000/1649371 (ऊपर) समाधान काम मैं निम्न विधियों (और बाद में सभी कॉल्स) को संशोधित करने के लिए किया था
@SuppressWarnings("all")
protected int getItemSpanSize(RecyclerView parent, View view, int childIndex) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex);
} else if (mgr instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).isFullSpan() ? spanCount : 1;
} else if (mgr instanceof LinearLayoutManager) {
return 1;
}
return -1;
}
@SuppressWarnings("all")
protected int getItemSpanIndex(RecyclerView parent, View view, int childIndex) {
RecyclerView.LayoutManager mgr = parent.getLayoutManager();
if (mgr instanceof GridLayoutManager) {
return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount);
} else if (mgr instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
} else if (mgr instanceof LinearLayoutManager) {
return 0;
}
return -1;
}
इस लिंक ने मेरे लिए उन सभी स्थितियों पर काम किया, जो आप इस कोशिश कर सकते हैं।
यदि आपके पास टॉगल स्विच है जो सूची में ग्रिड के बीच टॉगल करता है, तो recyclerView.removeItemDecoration()
किसी भी नए आइटम सजावट को स्थापित करने से पहले कॉल करना न भूलें । यदि नहीं तो रिक्ति के लिए नई गणना गलत होगी।
कुछ इस तरह।
recyclerView.removeItemDecoration(gridItemDecorator)
recyclerView.removeItemDecoration(listItemDecorator)
if (showAsList){
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.addItemDecoration(listItemDecorator)
}
else{
recyclerView.layoutManager = GridLayoutManager(this, spanCount)
recyclerView.addItemDecoration(gridItemDecorator)
}
यदि आप GridLayoutManager के साथ हैडर का उपयोग कर रहे हैं , तो ग्रिड के बीच अंतर करने के लिए कोटलिन में लिखे गए इस कोड का उपयोग करें :
inner class SpacesItemDecoration(itemSpace: Int) : RecyclerView.ItemDecoration() {
var space: Int = itemSpace
override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent!!.getChildAdapterPosition(view)
val viewType = parent.adapter.getItemViewType(position)
//check to not to set any margin to header item
if (viewType == GridViewAdapter.TYPE_HEADER) {
outRect!!.top = 0
outRect.left = 0
outRect.right = 0
outRect.bottom = 0
} else {
outRect!!.left = space
outRect.right = space
outRect.bottom = space
if (parent.getChildLayoutPosition(view) == 0) {
outRect.top = space
} else {
outRect.top = 0
}
}
}
}
और पास ItemDecoration
के recyclerview
रूप में
mIssueGridView.addItemDecoration(SpacesItemDecoration(10))
बच्चों के लिए कार्डव्यू का उपयोग करते समय आइटम के बीच की जगहों के साथ समस्या को ऐप को सेट करके हल किया जा सकता है: cardUseCompatPadding to true।
बड़े मार्जिन के लिए आइटम ऊंचाई बढ़ाना। कार्ड एलिवेशन वैकल्पिक है (डिफ़ॉल्ट मान का उपयोग करें)।
<androidx.cardview.widget.CardView
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardUseCompatPadding="true"
app:cardElevation="2dp">
धन्यवाद एडवर्डा का जवाब https://stackoverflow.com/a/30701422/2227031
एक और ध्यान देने वाली बात यह है कि:
यदि कुल रिक्ति और कुल आइटमविडिथ स्क्रीन की चौड़ाई के बराबर नहीं है, तो आपको एडवांटेज को एडजस्ट करने की भी आवश्यकता है, उदाहरण के लिए, ऑनपॉइंट ऑनबाइंडहोल्डर विधि
Utils.init(_mActivity);
int width = 0;
if (includeEdge) {
width = ScreenUtils.getScreenWidth() - spacing * (spanCount + 1);
} else {
width = ScreenUtils.getScreenWidth() - spacing * (spanCount - 1);
}
int itemWidth = width / spanCount;
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) holder.imageViewAvatar.getLayoutParams();
// suppose the width and height are the same
layoutParams.width = itemWidth;
layoutParams.height = itemWidth;
holder.imageViewAvatar.setLayoutParams(layoutParams);
एक कोटलिन संस्करण जिसे मैंने एडवर्डा के महान जवाब के आधार पर बनाया है
class RecyclerItemDecoration(private val spanCount: Int, private val spacing: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val spacing = Math.round(spacing * parent.context.resources.displayMetrics.density)
val position = parent.getChildAdapterPosition(view)
val column = position % spanCount
outRect.left = spacing - column * spacing / spanCount
outRect.right = (column + 1) * spacing / spanCount
outRect.top = if (position < spanCount) spacing else 0
outRect.bottom = spacing
}
}
GridLayoutManager
और ओवरराइडिंगgenerateDefaultLayoutParams()
और परिजनों की कोशिश की है ?