Reputation: 231
I'm using RecyclerView with StaggeredGridLayoutManager to make a two-column list. But how to set a right margin between left column and right column. I have used this code to make right margin from top, but how to solve double space between to columns.
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 or second item to avoid double space between items
// Add top margin only for the first or second item to avoid double space between items
if((parent.getChildCount() > 0 && parent.getChildPosition(view) == 0)
|| (parent.getChildCount() > 1 && parent.getChildPosition(view) == 1))
outRect.top = space;
}
And in Activity:
recyclerView.addItemDecoration(new SpacesItemDecoration(20));
I've tried to use view.getX()
, it always return 0.
Can anyone help me? Many thanks!
Upvotes: 20
Views: 12152
Reputation: 151
The following code handles StaggeredGridLayout without problems(at least from my experience). You only need to give spacing to SpaceItemDecorator.
class SpaceItemDecoration(private val spacing: Int) :
RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val lm = parent.layoutManager as StaggeredGridLayoutManager
val lp = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
val spanCount = lm.spanCount
val spanIndex = lp.spanIndex
val positionLayout = lp.viewLayoutPosition
val itemCount = lm.itemCount
val position = parent.getChildAdapterPosition(view)
outRect.right = spacing / 2
outRect.left = spacing / 2
outRect.top = spacing / 2
outRect.bottom = spacing / 2
if (spanIndex == 0) outRect.left = spacing
if (position < spanCount) outRect.top = spacing
if (spanIndex == (spanCount - 1)) outRect.right = spacing
if (positionLayout > (itemCount - spanCount)) outRect.bottom = spacing
}
}
You can get spacing in pixels like this:
val spacingInPixels = resources.getDimensionPixelSize(R.dimen.grid_layout_margin)
And then you just need to assign item decoration to your recycler view:
recycler_view.addItemDecoration(
SpaceItemDecoration(
spacingInPixels
)
)
Upvotes: 0
Reputation: 403
I solved this in 3 steps by the following way:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/margin_small"
android:layout_marginEnd="@dimen/margin_small"
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
app:spanCount="2" />
class SpaceItemDecorator : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val layoutParams = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
val spanIndex = layoutParams.spanIndex
if (spanIndex == 0) {
val marginParentEnd = parent.marginEnd
layoutParams.marginEnd = marginParentEnd / 2
} else {
val marginParentStart = parent.marginStart
layoutParams.marginStart = marginParentStart / 2
}
view.layoutParams = layoutParams
}
}
recyclerview.addItemDecoration(SpaceItemDecorator())
That is all, without margins/paddings hardcode and in Koltin xD
note: It is important not to establish any horizontal margin in the child's view layout
Upvotes: 0
Reputation: 539
It is possible to set padding to the two sides of the RecyclerView:
myRecyclerView.setPadding(10,0,10,0);
In this way you can equalize the space on the sides and between the columns.
Upvotes: 1
Reputation: 61
public class EqualGapItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
public EqualGapItemDecoration(int spanCount, int spacing) {
this.spanCount = spanCount;
this.spacing = spacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
if (layoutParams.isFullSpan()) {
outRect.set(0, 0, 0, 0);
} else {
int spanIndex = layoutParams.getSpanIndex();
int layoutPosition = layoutParams.getViewLayoutPosition();
int itemCount = parent.getAdapter().getItemCount();
boolean leftEdge = spanIndex == 0;
boolean rightEdge = spanIndex == (spanCount - 1);
boolean topEdge = spanIndex < spanCount;
boolean bottomEdge = layoutPosition >= (itemCount - spanCount);
int halfSpacing = spacing / 2;
outRect.set(
leftEdge ? spacing : halfSpacing,
topEdge ? spacing : halfSpacing,
rightEdge ? spacing : halfSpacing,
bottomEdge ? spacing : 0
);
}
}
}
Upvotes: 6
Reputation: 2606
I would slightly change and modify both of the answer for relative question
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private final int mSpace;
public SpacesItemDecoration(int space) {
this.mSpace = space;
}
@Override
public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
if (spanIndex == 0) {
outRect.left = 30;
outRect.right = 15;
} else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
outRect.left = 15;
outRect.right = 30;
}
outRect.bottom = 30;
// Add top margin only for the first item to avoid double space between items
if (parent.getChildAdapterPosition(view) == 0) {
outRect.top = 30;
outRect.right = 30;
}
}
}
Upvotes: 0
Reputation: 329
Set different left/right space in ItemDecoration according to spanindex only works well for item view has fixed size. If the item view contain image view set wrap_content in height, the item view may change span position, but span index not updated as you wish, the margin will be a mess.
My way is, using the symmetrical left/right space for view item.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// init
recyclerView = (RecyclerView) rv.findViewById(R.id.img_waterfall);
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(new MyAdapter(getContext()));
recyclerView.addItemDecoration(new SpacesItemDecoration(
getResources().getDimensionPixelSize(R.dimen.space)));
}
private static class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private final int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.bottom = 2 * space;
int pos = parent.getChildAdapterPosition(view);
outRect.left = space;
outRect.right = space;
if (pos < 2)
outRect.top = 2 * space;
}
}
Then set the same padding for RecylerView(left/right) android:paddingLeft="@dimen/space" android:paddingRight="@dimen/space"
<android.support.v7.widget.RecyclerView
android:id="@+id/img_waterfall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/space"
android:paddingRight="@dimen/space"/>
Upvotes: 0
Reputation: 81
In your case you can setting the both margin to the RecyclerView .But in my case there is a image match the screen width in the RecyclerView ,and I use the ItemDecoration to solve the problem.
class ViewItemDecoration extends RecyclerView.ItemDecoration {
public ViewItemDecoration() {
}
@Override
public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
int type = adapter.getItemViewType(position);
switch(type){
case YOUR_ITEMS:
if (spanIndex == 0) {
outRect.left = 10;
outRect.right = 5;
} else {//if you just have 2 span . Or you can use (staggeredGridLayoutManager.getSpanCount()-1) as last span
outRect.left = 5;
outRect.right = 10;
}
}
}
}
Upvotes: 8
Reputation: 1701
The following code will handle StaggeredGridLayoutManager, GridLayoutManager, and 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;
}
}
Upvotes: 10
Reputation: 231
I solved it myself by setting item margin and RecyclerView padding. Both margin and padding are half of expected space, so the problem disappeared.
Upvotes: 3