Reputation: 28407
This is an example of how it could have been done previously in the ListView
class, using the divider and dividerHeight parameters:
<ListView
android:id="@+id/activity_home_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/transparent"
android:dividerHeight="8dp"/>
However, I don't see such possibility in the RecyclerView
class.
<android.support.v7.widget.RecyclerView
android:id="@+id/activity_home_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
In that case, is it ok to define margins and/or add a custom divider view directly into a list item's layout or is there a better way to achieve my goal?
Upvotes: 1126
Views: 846757
Reputation: 4980
There's a MaterialDesign component for that, you can also adjust the insets and use it wherever you want:
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dividerInsetEnd="8dp"
app:dividerInsetStart="8dp"/>
Just add the MaterialDivider
component to your item layout (cell view) like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?android:attr/selectableItemBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<!-- Cell content -->
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dividerInsetEnd="8dp"
app:dividerInsetStart="8dp"/>
</LinearLayout>
The important thing here is to place it inside a vertical LinearLayout as a last child to show it behind the content of the cell.
Upvotes: 0
Reputation: 960
This is simple, and you don't need such complicated code:
DividerItemDecoration divider =
new DividerItemDecoration(mRVMovieReview.getContext(),
DividerItemDecoration.VERTICAL);
divider.setDrawable(ContextCompat.getDrawable(getBaseContext(),
R.drawable.line_divider));
mRVMovieReview.addItemDecoration(divider);
Add this in your drawable: line_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="1dp" />
<solid android:color="@android:color/black" />
</shape>
Upvotes: 55
Reputation: 260
If you want to add the same space for items, the simplest way is to add top+left padding for RecycleView
and right+bottom margins to card items.
dimens.xml
<resources>
<dimen name="divider">1dp</dimen>
</resources>
list_item.xml
<CardView
android:layout_marginBottom="@dimen/divider"
android:layout_marginRight="@dimen/divider">
...
</CardView>
list.xml
<RecyclerView
...
android:paddingLeft="@dimen/divider"
android:paddingTop="@dimen/divider" />
Upvotes: 5
Reputation: 2889
Might I direct your attention to this particular file on GitHub by Alex Fu: link
It's the DividerItemDecoration.java
example file "pulled straight from the support demos".
I was able to get divider lines nicely after importing this file in my project and add it as an item decoration to the recycler view.
Here's how my onCreateView
look like in my fragment containing the Recyclerview
:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_recycler_view, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
return rootView;
}
I'm sure additional styling can be done, but it's a starting point. :)
Upvotes: 277
Reputation: 701
Using RecyclerView with ItemDecorations - A Basic Separator Sample in Kotlin android
A complete sample including a builder, the possibility to add margins or use a resource color
class SeparatorDecoration constructor(ctx: Context, @ColorRes dividerColor: Int, heightDp: Float) :
RecyclerView.ItemDecoration() {
private val mPaints = Paint()
init {
mPaints.color = dividerColor
val thickness = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
heightDp,
ctx.resources.displayMetrics
)
mPaints.strokeWidth = thickness
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val params = view.layoutParams as RecyclerView.LayoutParams
// and add a separator to any view but the last one
val p = params.viewAdapterPosition
// and add a separator to any view but the last one
if (p < state.itemCount) outRect[0, 0, 0] =
mPaints.strokeWidth.toInt() // left, top, right, bottom
else outRect.setEmpty() // 0, 0, 0, 0
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// we set the stroke width before, so as to correctly draw the line we have to offset by width / 2
val offset = (mPaints.strokeWidth / 2).roundToInt()
// this will iterate over every visible view
for (i in 0 until parent.childCount) {
// get the view
val view: View = parent.getChildAt(i)
val params = view.layoutParams as RecyclerView.LayoutParams
// get the position
val position = params.viewAdapterPosition
// and finally draw the separator
if (position < state.itemCount) {
c.drawLine(
view.left.toFloat(),
(view.bottom + offset).toFloat(),
view.right.toFloat(), (view.bottom + offset).toFloat(), mPaints
)
}
}
}
}
The setup is easy. Just add your decorations along with the rest of the initial setup for your recyclerView.
val decoration = SeparatorDecoration(context, R.color.primaryColor, 1.5f)
recyclerview.addItemDecoration(decoration)
Upvotes: 2
Reputation: 4817
The newest approach is this one, being used for example like this in the onCreateView of a Fragment:
val recyclerView = rootView.findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = mListAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
rootView.context.let {
val dividerItemDecoration = MaterialDividerItemDecoration(
it,
MaterialDividerItemDecoration.VERTICAL
)
dividerItemDecoration.isLastItemDecorated = false
// https://github.com/material-components/material-components-android/blob/master/docs/components/Divider.md
// Needed if you did not set colorOnSurface in your theme because otherwise the default color would be pink_900 -> default according to Material should be colorOnSurface (12% opacity applied automatically on top).
// dividerItemDecoration.setDividerColorResource(it, R.color.colorDivider)
recyclerView.addItemDecoration(dividerItemDecoration)
}
I think you can forget all other solutions before.
Upvotes: 5
Reputation: 2852
If someone wants to set different values for spaceBetween, paddingLeft, paddingTop, paddingRight and paddingBottom.
class ItemPaddingDecoration(
private val spaceBetween: Int,
private val paddingLeft: Int = spaceBetween,
private val paddingTop: Int = spaceBetween,
private val paddingRight: Int = spaceBetween,
private val paddingBottom: Int = spaceBetween
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val position = parent.getChildAdapterPosition(view)
val orientation = when (val layoutManager = parent.layoutManager) {
is LinearLayoutManager -> {
layoutManager.orientation
}
is GridLayoutManager -> {
layoutManager.orientation
}
else -> {
RecyclerView.HORIZONTAL
}
}
if (orientation == RecyclerView.HORIZONTAL) {
when {
position == 0 -> {
outRect.set(paddingLeft, paddingTop, spaceBetween, paddingBottom)
}
position < parent.adapter!!.itemCount - 1 -> {
outRect.set(0, paddingTop, spaceBetween, paddingBottom)
}
else -> {
outRect.set(0, paddingTop, paddingRight, paddingBottom)
}
}
} else {
when {
position == 0 -> {
outRect.set(paddingLeft, paddingTop, paddingRight, paddingBottom)
}
position < parent.adapter!!.itemCount - 1 -> {
outRect.set(paddingLeft, 0, paddingRight, spaceBetween)
}
else -> {
outRect.set(paddingLeft, 0, paddingRight, paddingBottom)
}
}
}
}
}
Upvotes: 1
Reputation: 2569
If anyone is looking to only add, say, 10 dp spacing between items, you can do so by setting a drawable to DividerItemDecoration
:
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
recyclerView.getContext(),
layoutManager.getOrientation()
);
dividerItemDecoration.setDrawable(
ContextCompat.getDrawable(getContext(), R.drawable.divider_10dp)
);
recyclerView.addItemDecoration(dividerItemDecoration);
Where divider_10dp
is a drawable resource containing:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<size android:height="10dp"/>
<solid android:color="@android:color/transparent"/>
</shape>
Upvotes: 29
Reputation: 3943
Here's how I would do it in Kotlin, for a simple space between Items with a default size of 10 dp:
class SimpleItemDecoration(context: Context, space: Int = 10) : RecyclerView.ItemDecoration() {
private val spaceInDp = ConvertUtil.dpToPx(context, space)
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
outRect.left = spaceInDp
outRect.right = spaceInDp
outRect.bottom = spaceInDp
// Add top margin only for the first item to avoid double space between items
if (parent.getChildAdapterPosition(view) == 0) {
outRect.top = spaceInDp
}
}
}
And here is the dpToPx method:
fun dpToPx(context: Context, dp: Int): Int {
return (dp * context.resources.displayMetrics.density).toInt()
}
and then add it to the RecyclerView like this: (thanks @MSpeed)
recyclerView.addItemDecoration(SimpleItemDecoration(context))
Upvotes: 4
Reputation: 60913
Instead of creating a shape xml
for changing the divider height and color, you can create it programmatically like:
val divider = DividerItemDecoration(
context,
DividerItemDecoration.VERTICAL)
divider.setDrawable(ShapeDrawable().apply {
intrinsicHeight = resources.getDimensionPixelOffset(R.dimen.dp_15)
paint.color = Color.RED // Note:
// Currently (support version 28.0.0), we
// can not use tranparent color here. If
// we use transparent, we still see a
// small divider line. So if we want
// to display transparent space, we
// can set color = background color
// or we can create a custom ItemDecoration
// instead of DividerItemDecoration.
})
recycler_devices.addItemDecoration(divider)
Upvotes: 20
Reputation: 22832
PrimeAdapter
By using PrimeAdapter, handling dividers in RecyclerView
s could be so simple. It provides multiple functionality and more flexibility to create and manage dividers.
You can create an adapter as following (please see the complete documentation about usage in GitHub):
val adapter = PrimeAdapter.with(recyclerView)
.setLayoutManager(LinearLayoutManager(activity))
.set()
.build(ActorAdapter::class.java)
After creating an adapter instance, you can simply add a divider simply by using:
//----- Default divider:
adapter.setDivider()
//----- Divider with a custom drawable:
adapter.setDivider(ContextCompat.getDrawable(context, R.drawable.divider))
//----- Divider with a custom color:
adapter.setDivider(Color.RED)
//----- Divider with a custom color and a custom inset:
adapter.setDivider(Color.RED, insetLeft = 16, insetRight = 16)
//----- Deactivate dividers:
adapter.setDivider(null)
Upvotes: 1
Reputation: 4108
Just add this line after init in the recycler view object.
In Fragment:
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(),android.R.drawable.divider_horizontal_bright));
In Activity
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,android.R.drawable.divider_horizontal_bright));
Divider Item Decoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int VERTICAL_LIST = 0;
public DividerItemDecoration(ListActivity listActivity, Object p1) {
}
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
/**
* Default divider will be used
*/
public DividerItemDecoration(Context context) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
}
/**
* Custom divider will be used
*/
public DividerItemDecoration(Context context, int resId) {
mDivider = ContextCompat.getDrawable(context, resId);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
Upvotes: -2
Reputation: 3629
Use this class to set divider in your RecyclerView
.
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
}
}
}
}
Upvotes: 1
Reputation: 860
public class CommonItemSpaceDecoration extends RecyclerView.ItemDecoration {
private int mSpace = 0;
private boolean mVerticalOrientation = true;
public CommonItemSpaceDecoration(int space) {
this.mSpace = space;
}
public CommonItemSpaceDecoration(int space, boolean verticalOrientation) {
this.mSpace = space;
this.mVerticalOrientation = verticalOrientation;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.top = SizeUtils.dp2px(view.getContext(), mSpace);
if (mVerticalOrientation) {
if (parent.getChildAdapterPosition(view) == 0) {
outRect.set(0, SizeUtils.dp2px(view.getContext(), mSpace), 0, SizeUtils.dp2px(view.getContext(), mSpace));
} else {
outRect.set(0, 0, 0, SizeUtils.dp2px(view.getContext(), mSpace));
}
} else {
if (parent.getChildAdapterPosition(view) == 0) {
outRect.set(SizeUtils.dp2px(view.getContext(), mSpace), 0, 0, 0);
} else {
outRect.set(SizeUtils.dp2px(view.getContext(), mSpace), 0, SizeUtils.dp2px(view.getContext(), mSpace), 0);
}
}
}
}
This will add space in every item's top and bottom (or left and right). Then you can set it to your recyclerView
.
recyclerView.addItemDecoration(new CommonItemSpaceDecoration(16));
public class SizeUtils {
public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
Upvotes: 5
Reputation: 1434
public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
private boolean verticalOrientation = true;
private int space = 10;
public VerticalItemDecoration(int value, boolean verticalOrientation) {
this.space = value;
this.verticalOrientation = verticalOrientation;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
// Skip first item in the list
if (parent.getChildAdapterPosition(view) != 0) {
if (verticalOrientation) {
outRect.set(space, 0, 0, 0);
} else if (!verticalOrientation) {
outRect.set(0, space, 0, 0);
}
}
}
}
mCompletedShippingRecyclerView.addItemDecoration(new VerticalItemDecoration(20,false));
Upvotes: 1
Reputation: 524
I have a very simple way of adding a divider in RecyclerView. Use a custom adapter to modify the recycler view layout and then along with the recycler view items add LinearLayout with a background color (which will be the divider color) and add a height of 1 dp (or as per your requirement) and width to match parent.
Here is a sample code.
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="18dp">
<TextView
android:id="@+id/list_row_SNO"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight=".8"
android:layout_gravity="end"
android:text="44."
android:textAlignment="center"
android:textSize="24sp"
android:textColor="@color/colorBlack"
android:fontFamily="sans-serif-condensed" />
<TextView
android:id="@+id/list_row_Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight=".2"
android:layout_gravity="start"
android:text="Student's application for leave and this what"
android:textAlignment="textStart"
android:textSize="24sp"
android:textColor="@color/colorBlack"
android:fontFamily="sans-serif-condensed" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorHighlight">
</LinearLayout>
</LinearLayout>
Upvotes: 1
Reputation: 432
One of the ways is by using the cardview and recycler view together. We can easily add an effect, like a divider. Example: Create dynamic lists with RecyclerView
And another is by adding a view as a divider to a list_item_layout of a recycler view.
<View
android:id="@+id/view1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent" />
Upvotes: 4
Reputation: 3098
You can easily add it programmatically.
If your Layout Manager is Linearlayout then you can use:
DividerItemDecoration is a RecyclerView.ItemDecoration that can be used as a divider between items of a LinearLayoutManager. It supports both HORIZONTAL and VERTICAL orientations.
mDividerItemDecoration =
new DividerItemDecoration(recyclerView.getContext(),
mLayoutManager.getOrientation());
recyclerView.addItemDecoration(mDividerItemDecoration);
Upvotes: 7
Reputation: 871
For GridLayoutManager
I use this:
public class GridSpacesItemDecoration : RecyclerView.ItemDecoration
{
private int space;
public GridSpacesItemDecoration(int space) {
this.space = space;
}
public override void GetItemOffsets(Android.Graphics.Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
{
var position = parent.GetChildLayoutPosition(view);
/// Only for GridLayoutManager Layouts
var manager = parent.GetLayoutManager() as GridLayoutManager;
if (parent.GetChildLayoutPosition(view) < manager.SpanCount)
outRect.Top = space;
if (position % 2 != 0) {
outRect.Right = space;
}
outRect.Left = space;
outRect.Bottom = space;
}
}
This works for any span count you have.
Upvotes: 5
Reputation: 1488
With support library v25.0.0 there finally is a default implementation of basic horizontal and vertical dividers available!
Upvotes: 15
Reputation: 274
RecyclerView doesn't provide a straightforward interface for drawing dividers of lists. But actually it provides us a much more flexible way to draw dividers.
We use RecyclerView.ItemDecoration to decorate RecyclerView's tiles with dividers or anything you want. That is also why it is called ItemDecoration.
As described in Material Design Guidelines:
The divider sits within the baseline of the tile.
So if you want to follow the Material Design Guidelines, you don't need extra space to draw the dividers. Just draw them on the tiles. However, you have the right to do whatever you want to do. So I implemented one which gives you the ability to set insets as well as draw on/underneath the tiles.
public class InsetDivider extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Paint mPaint;
// in pixel
private int mDividerHeight;
// left inset for vertical list, top inset for horizontal list
private int mFirstInset;
// right inset for vertical list, bottom inset for horizontal list
private int mSecondInset;
private int mColor;
private int mOrientation;
// set it to true to draw divider on the tile, or false to draw beside the tile.
// if you set it to false and have inset at the same time, you may see the background of
// the parent of RecyclerView.
private boolean mOverlay;
private InsetDivider() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
}
public int getDividerHeight() {
return mDividerHeight;
}
public void setDividerHeight(int dividerHeight) {
this.mDividerHeight = dividerHeight;
}
public int getFirstInset() {
return mFirstInset;
}
public void setFirstInset(int firstInset) {
this.mFirstInset = firstInset;
}
public int getSecondInset() {
return mSecondInset;
}
public void setSecondInset(int secondInset) {
this.mSecondInset = secondInset;
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
this.mColor = color;
mPaint.setColor(color);
}
public int getOrientation() {
return mOrientation;
}
public void setOrientation(int orientation) {
this.mOrientation = orientation;
}
public boolean getOverlay() {
return mOverlay;
}
public void setOverlay(boolean overlay) {
this.mOverlay = overlay;
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
protected void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft() + mFirstInset;
final int right = parent.getWidth() - parent.getPaddingRight() - mSecondInset;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
if (parent.getChildAdapterPosition(child) == (parent.getAdapter().getItemCount() - 1)) {
continue;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int bottom;
final int top;
if (mOverlay) {
bottom = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
top = bottom - mDividerHeight;
} else {
top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
bottom = top + mDividerHeight;
}
c.drawRect(left, top, right, bottom, mPaint);
}
}
protected void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop() + mFirstInset;
final int bottom = parent.getHeight() - parent.getPaddingBottom() - mSecondInset;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
if (parent.getChildAdapterPosition(child) == (parent.getAdapter().getItemCount() - 1)) {
continue;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int right;
final int left;
if (mOverlay) {
right = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
left = right - mDividerHeight;
} else {
left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
right = left + mDividerHeight;
}
c.drawRect(left, top, right, bottom, mPaint);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOverlay) {
super.getItemOffsets(outRect, view, parent, state);
return;
}
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDividerHeight);
} else {
outRect.set(0, 0, mDividerHeight, 0);
}
}
/**
* Handy builder for creating {@link InsetDivider} instance.
*/
public static class Builder {
private Context mContext;
private int mDividerHeight;
private int mFirstInset;
private int mSecondInset;
private int mColor;
private int mOrientation;
private boolean mOverlay = true; // set default to true to follow Material Design Guidelines
public Builder(Context context) {
mContext = context;
}
public Builder dividerHeight(int dividerHeight) {
mDividerHeight = dividerHeight;
return this;
}
public Builder insets(int firstInset, int secondInset) {
mFirstInset = firstInset;
mSecondInset = secondInset;
return this;
}
public Builder color(@ColorInt int color) {
mColor = color;
return this;
}
public Builder orientation(int orientation) {
mOrientation = orientation;
return this;
}
public Builder overlay(boolean overlay) {
mOverlay = overlay;
return this;
}
public InsetDivider build() {
InsetDivider insetDivider = new InsetDivider();
if (mDividerHeight == 0) {
// Set default divider height to 1dp.
insetDivider.setDividerHeight(mContext.getResources().getDimensionPixelSize(R.dimen.divider_height));
} else if (mDividerHeight > 0) {
insetDivider.setDividerHeight(mDividerHeight);
} else {
throw new IllegalArgumentException("Divider's height can't be negative.");
}
insetDivider.setFirstInset(mFirstInset < 0 ? 0 : mFirstInset);
insetDivider.setSecondInset(mSecondInset < 0 ? 0 : mSecondInset);
if (mColor == 0) {
throw new IllegalArgumentException("Don't forget to set color");
} else {
insetDivider.setColor(mColor);
}
if (mOrientation != InsetDivider.HORIZONTAL_LIST && mOrientation != InsetDivider.VERTICAL_LIST) {
throw new IllegalArgumentException("Invalid orientation");
} else {
insetDivider.setOrientation(mOrientation);
}
insetDivider.setOverlay(mOverlay);
return insetDivider;
}
}
}
And you can use it like this:
ItemDecoration divider = new InsetDivider.Builder(this)
.orientation(InsetDivider.VERTICAL_LIST)
.dividerHeight(getResources().getDimensionPixelSize(R.dimen.divider_height))
.color(getResources().getColor(R.color.colorAccent))
.insets(getResources().getDimensionPixelSize(R.dimen.divider_inset), 0)
.overlay(true)
.build();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(divider);
I also wrote a demo app about how to implement ItemDecoration and how to use them. You can check out my GitHub repository Dividers-For-RecyclerView. There are three implementations in it:
Upvotes: 2
Reputation: 1386
I have added a line in a list item like below:
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/dividerColor"/>
"1px" will draw the thin line.
If you want to hide the divider for the last row, then use divider.setVisiblity(View.GONE);
on the onBindViewHolder for the last list Item.
Upvotes: 4
Reputation: 17976
Since there is no right way to implement this yet properly using Material Design, I just did the following trick to add a divider on the list item directly:
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/dividerColor"/>
Upvotes: 24
Reputation: 1958
We can decorate the items using various decorators attached to the recyclerview such as the DividerItemDecoration:
Simply use the following ...taken from the answer byEyesClear:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
/**
* Default divider will be used
*/
public DividerItemDecoration(Context context) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
}
/**
* Custom divider will be used
*/
public DividerItemDecoration(Context context, int resId) {
mDivider = ContextCompat.getDrawable(context, resId);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
And then use the above as follows:
RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
recyclerView.addItemDecoration(itemDecoration);
This will display dividers between each item within the list as shown below:
And for those of who are looking for more details can check out this guide Using the RecyclerView _ CodePath Android Cliffnotes.
Some answers here suggest the use of margins, but the catch is that:
If you add both top and bottom margins, they will appear both added between items and they will be too large. If you only add either, there will be no margin either at the top or the bottom of the whole list. If you add half of the distance at the top, half at the bottom, the outer margins will be too small.
Thus, the only aesthetically correct solution is the divider that the system knows where to apply properly: between items, but not above or below items.
Upvotes: 6
Reputation: 2306
Here is a simple hack to add a divider
Just add a background to the layout of your recycler item as follows
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shape_border"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/imageViewContactLogo"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginRight="10dp"
android:src="@drawable/ic_user" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.92"
android:gravity="center|start"
android:orientation="vertical">
<TextView
android:id="@+id/textViewContactName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textViewStatusOrNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:singleLine="true"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<TextView
android:id="@+id/textViewUnreadCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:padding="5dp"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/red"
android:textSize="22sp" />
<Button
android:id="@+id/buttonInvite"
android:layout_width="54dp"
android:layout_height="wrap_content"
android:background="@drawable/ic_add_friend" />
</LinearLayout>
Create the following shape_border.xml file in the drawable folder:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="270"
android:centerColor="@android:color/transparent"
android:centerX="0.01"
android:startColor="#000" />
</shape>
Here is the final result - a RecyclerView with divider.
Upvotes: 12
Reputation: 1990
The RecyclerView
is a bit different from the ListView
. Actually, the RecyclerView
needs a ListView
like structure in it. For example, a LinearLayout
. The LinearLayout
has parameters for the dividing each element.
In the code below I have a RecyclerView
comprised of CardView
objects within a LinearLayout
with a "padding" that will put some space between items. Make that space really small and you get a line.
Here's the Recycler view in file recyclerview_layout.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".ToDoList">
<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
android:id="@+id/todo_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
And here is what each item looks like (and it shows as divided due to the android:padding in the LinearLayout that surrounds everything) in another file: cards_layout.xml
<?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"
**android:padding="@dimen/activity_vertical_margin"**>
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="100dp"
android:elevation="30dp"
card_view:cardElevation="3dp">
<TextView
android:id="@+id/info_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.v7.widget.CardView>
</LinearLayout>
Upvotes: 3
Reputation: 26071
The simple one is to set the background color for RecyclerView and a different background color for items. Here is an example...
<android.support.v7.widget.RecyclerView
android:background="#ECEFF1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"/>
And the TextView item (it can be anything though) with bottom margin "x" dp or px.
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="1dp"
android:background="#FFFFFF"/>
The output...
Upvotes: 116
Reputation: 6863
Here's my lazy approach, but it works: wrap the CardView in a layout and set a padding/margin on the parent layout to mimic the divider, and force the normal divider to null.
<LinearLayout
android:id="@+id/entry_item_layout_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="<divider_size>" > // This is the divider
<CardView
android:layout_width="<width_size>"
android:layout_height="<height_size>">
...
</CardView>
</LinearLayout
<RecyclerView
android:divider="@null"
android:layout_width="<width_size>"
android:layout_height="<height_size>"
...
/>
Upvotes: 0
Reputation: 4279
For those who are looking just for spaces between items in the RecyclerView
, see my approach where you get equal spaces between all items, except in the first and last items where I gave a bigger padding. I only apply padding to left/right in a horizontal LayoutManager
and to top/bottom in a vertical LayoutManager
.
public class PaddingItemDecoration extends RecyclerView.ItemDecoration {
private int mPaddingPx;
private int mPaddingEdgesPx;
public PaddingItemDecoration(Activity activity) {
final Resources resources = activity.getResources();
mPaddingPx = (int) resources.getDimension(R.dimen.paddingItemDecorationDefault);
mPaddingEdgesPx = (int) resources.getDimension(R.dimen.paddingItemDecorationEdge);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
final int itemPosition = parent.getChildAdapterPosition(view);
if (itemPosition == RecyclerView.NO_POSITION) {
return;
}
int orientation = getOrientation(parent);
final int itemCount = state.getItemCount();
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
/** Horizontal */
if (orientation == LinearLayoutManager.HORIZONTAL) {
/** All positions */
left = mPaddingPx;
right = mPaddingPx;
/** First position */
if (itemPosition == 0) {
left += mPaddingEdgesPx;
}
/** Last position */
else if (itemCount > 0 && itemPosition == itemCount - 1) {
right += mPaddingEdgesPx;
}
}
/** Vertical */
else {
/** All positions */
top = mPaddingPx;
bottom = mPaddingPx;
/** First position */
if (itemPosition == 0) {
top += mPaddingEdgesPx;
}
/** Last position */
else if (itemCount > 0 && itemPosition == itemCount - 1) {
bottom += mPaddingEdgesPx;
}
}
if (!isReverseLayout(parent)) {
outRect.set(left, top, right, bottom);
} else {
outRect.set(right, bottom, left, top);
}
}
private boolean isReverseLayout(RecyclerView parent) {
if (parent.getLayoutManager() instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getReverseLayout();
} else {
throw new IllegalStateException("PaddingItemDecoration can only be used with a LinearLayoutManager.");
}
}
private int getOrientation(RecyclerView parent) {
if (parent.getLayoutManager() instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getOrientation();
} else {
throw new IllegalStateException("PaddingItemDecoration can only be used with a LinearLayoutManager.");
}
}
}
<resources>
<dimen name="paddingItemDecorationDefault">10dp</dimen>
<dimen name="paddingItemDecorationEdge">20dp</dimen>
</resources>
Upvotes: 13
Reputation: 1850
I think using a simple divider will help you
To add divider to each item:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="#999999" />
</shape>
I used this example to define this class:
https://gist.github.com/polbins/e37206fbc444207c0e92
package com.example.myapp;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.example.myapp.R;
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration{
private Drawable mDivider;
public SimpleDividerItemDecoration(Resources resources) {
mDivider = resources.getDrawable(R.drawable.line_divider);
}
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
RecyclerView myRecyclerView = (RecyclerView) layout.findViewById(R.id.my_recycler_view);
myRecyclerView.addItemDecoration(new SimpleDividerItemDecoration(getResources()));
....
}
You just need to add padding property to your item view
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="4dp"
>
..... item structure
</RelativeLayout>
Upvotes: 43