Reputation: 6675
Below is how I'm doing the spacing for RecyclerView items. It's designed to work with both grids and lists. The spacing works.
What I can't figure out is how to insert a divider line as well. Any help doing so would be greatly appreciated.
SIDE NOTE: if you have a better way to implement the spacing than what I'm currently doing, I'd be very grateful as well :)
public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
private int numOfColumns;
private int listSize;
private int offsetInDp;
private boolean isGridView;
private boolean canScrollHorizontally;
private boolean isBottomRow = false;
public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int listSize, int offsetInDp) {
this(manager, 1, listSize, offsetInDp);
}
public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int numOfColumns, int listSize, int offsetInDp) {
this.numOfColumns = numOfColumns;
this.listSize = listSize;
this.offsetInDp = PixelConversionUtils.dpToPx(offsetInDp);
this.isGridView = manager instanceof GridLayoutManager;
this.canScrollHorizontally = manager.canScrollHorizontally();
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
// only do left/right spacing if grid or horizontal list
if (isGridView || canScrollHorizontally) {
outRect.left = offsetInDp;
outRect.right = offsetInDp;
}
// only do top/bottom spacing if grid or vertical list
if (isGridView || !canScrollHorizontally) {
int pos = parent.getChildAdapterPosition(view);
boolean isNotTopRow = pos >= numOfColumns;
// Don't add top spacing to top row
if (isNotTopRow) {
outRect.top = offsetInDp;
}
int columnIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
if (pos >= (listSize - numOfColumns) && columnIndex == 0) {
isBottomRow = true;
}
// Don't add bottom spacing to bottom row
if (!isBottomRow && pos < (listSize - numOfColumns)) {
outRect.bottom = offsetInDp;
}
}
}
}
here's a quick visual of what I'm looking to do:
here's what I have:
here's what I want:
Upvotes: 0
Views: 1610
Reputation: 528
You can achieve desired look this way:
first, create a divider Drawable, for this example I've used a simple shape, but you could use default line divider or any other drawable:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="2dp" />
<size android:width="2dp" />
<solid android:color="#000000" />
</shape>
second, in your ItemOffsetDecoration
declare Drawable and initialize it:
public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
...
public ItemOffsetDecoration(...) {
mDivider = ContextCompat.getDrawable(context, R.drawable.item_divider);
}
}
third, override onDrawOver() method:
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (isGridView) {
drawVerticalDivider(c, parent);
} else {
drawVerticalDivider(c, parent);
drawHorizontalDivider(c, parent);
}
}
where drawVerticalDivider()
& drawHorizontalDivider()
are (might be a good idea to refactor them into the single method and control direction of the divider via parameter):
public void drawVerticalDivider(Canvas c, RecyclerView parent) {
if (parent.getChildCount() == 0) return;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getLeft() - params.leftMargin - offsetInDp;
int right = child.getRight() + params.rightMargin + offsetInDp;
int top = child.getBottom() + params.bottomMargin + offsetInDp;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontalDivider(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin + offsetInDp;
int right = left + mDivider.getIntrinsicWidth();
int top = child.getTop() - params.topMargin - offsetInDp;
int bottom = child.getBottom() + params.bottomMargin + offsetInDp;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
Result for the Linear and Grid LayoutManagers:
Upvotes: 3
Reputation: 763
haha……actualy,i had try like this for Divider ,although with a bit funny : first make you recycleview backgroud with Deep color,and make item_view backgroud white,then marginbottom for every item -> I'm serious, do not vote down :)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_marginBottom="2dp"
android:layout_height="wrap_content">
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
Upvotes: 0
Reputation: 1323
Try placing the following XML snippet to get a divider:
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="72dp"
android:layout_marginRight="16dp"
android:background="123e4152"/>
You can put this in the recyclerView's item layout beneath your items. Also Play around with the margins and background to suit your list.
Upvotes: 0