贴两个RecyclerView分割线的工具类,一个是LinearLayoutManager用的,一个是GridLayoutManager用的:
public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {
private MarginProvider mMarginProvider;
protected HorizontalDividerItemDecoration(Builder builder) {
super(builder);
mMarginProvider = builder.mMarginProvider;
}
@Override
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
Rect bounds = new Rect(0, 0, 0, 0);
int transitionX = (int) ViewCompat.getTranslationX(child);
int transitionY = (int) ViewCompat.getTranslationY(child);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
bounds.left = parent.getPaddingLeft() +
mMarginProvider.dividerLeftMargin(position, parent) + transitionX;
bounds.right = parent.getWidth() - parent.getPaddingRight() -
mMarginProvider.dividerRightMargin(position, parent) + transitionX;
int dividerSize = getDividerSize(position, parent);
boolean isReverseLayout = isReverseLayout(parent);
if (mDividerType == DividerType.DRAWABLE) {
// set top and bottom position of divider
if (isReverseLayout) {
bounds.bottom = child.getTop() - params.topMargin + transitionY;
bounds.top = bounds.bottom - dividerSize;
} else {
bounds.top = child.getBottom() + params.bottomMargin + transitionY;
bounds.bottom = bounds.top + dividerSize;
}
} else {
// set center point of divider
int halfSize = dividerSize / 2;
if (isReverseLayout) {
bounds.top = child.getTop() - params.topMargin - halfSize + transitionY;
} else {
bounds.top = child.getBottom() + params.bottomMargin + halfSize + transitionY;
}
bounds.bottom = bounds.top;
}
if (mPositionInsideItem) {
if (isReverseLayout) {
bounds.top += dividerSize;
bounds.bottom += dividerSize;
} else {
bounds.top -= dividerSize;
bounds.bottom -= dividerSize;
}
}
return bounds;
}
@Override
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
if (mPositionInsideItem) {
outRect.set(0, 0, 0, 0);
return;
}
if (isReverseLayout(parent)) {
outRect.set(0, getDividerSize(position, parent), 0, 0);
} else {
outRect.set(0, 0, 0, getDividerSize(position, parent));
}
}
private int getDividerSize(int position, RecyclerView parent) {
if (mPaintProvider != null) {
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
} else if (mSizeProvider != null) {
return mSizeProvider.dividerSize(position, parent);
} else if (mDrawableProvider != null) {
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
return drawable.getIntrinsicHeight();
}
throw new RuntimeException("failed to get size");
}
/**
* Interface for controlling divider margin
*/
public interface MarginProvider {
/**
* Returns left margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return left margin
*/
int dividerLeftMargin(int position, RecyclerView parent);
/**
* Returns right margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return right margin
*/
int dividerRightMargin(int position, RecyclerView parent);
}
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
private MarginProvider mMarginProvider = new MarginProvider() {
@Override
public int dividerLeftMargin(int position, RecyclerView parent) {
return 0;
}
@Override
public int dividerRightMargin(int position, RecyclerView parent) {
return 0;
}
};
public Builder(Context context) {
super(context);
}
public Builder margin(final int leftMargin, final int rightMargin) {
return marginProvider(new MarginProvider() {
@Override
public int dividerLeftMargin(int position, RecyclerView parent) {
return leftMargin;
}
@Override
public int dividerRightMargin(int position, RecyclerView parent) {
return rightMargin;
}
});
}
public Builder margin(int horizontalMargin) {
return margin(horizontalMargin, horizontalMargin);
}
public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) {
return margin(mResources.getDimensionPixelSize(leftMarginId),
mResources.getDimensionPixelSize(rightMarginId));
}
public Builder marginResId(@DimenRes int horizontalMarginId) {
return marginResId(horizontalMarginId, horizontalMarginId);
}
public Builder marginProvider(MarginProvider provider) {
mMarginProvider = provider;
return this;
}
public HorizontalDividerItemDecoration build() {
checkBuilderParams();
return new HorizontalDividerItemDecoration(this);
}
}
}
HorizontalDividerItemDecoration继承了FlexibleDividerDecoration,FlexibleDividerDecoration 代码如下:
public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
private static final int DEFAULT_SIZE = 2;
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
protected enum DividerType {
DRAWABLE, PAINT, COLOR
}
protected DividerType mDividerType = DividerType.DRAWABLE;
protected VisibilityProvider mVisibilityProvider;
protected PaintProvider mPaintProvider;
protected ColorProvider mColorProvider;
protected DrawableProvider mDrawableProvider;
protected SizeProvider mSizeProvider;
protected boolean mShowLastDivider;
protected boolean mPositionInsideItem;
private Paint mPaint;
protected FlexibleDividerDecoration(Builder builder) {
if (builder.mPaintProvider != null) {
mDividerType = DividerType.PAINT;
mPaintProvider = builder.mPaintProvider;
} else if (builder.mColorProvider != null) {
mDividerType = DividerType.COLOR;
mColorProvider = builder.mColorProvider;
mPaint = new Paint();
setSizeProvider(builder);
} else {
mDividerType = DividerType.DRAWABLE;
if (builder.mDrawableProvider == null) {
TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
final Drawable divider = a.getDrawable(0);
a.recycle();
mDrawableProvider = new DrawableProvider() {
@Override
public Drawable drawableProvider(int position, RecyclerView parent) {
return divider;
}
};
} else {
mDrawableProvider = builder.mDrawableProvider;
}
mSizeProvider = builder.mSizeProvider;
}
mVisibilityProvider = builder.mVisibilityProvider;
mShowLastDivider = builder.mShowLastDivider;
mPositionInsideItem = builder.mPositionInsideItem;
}
private void setSizeProvider(Builder builder) {
mSizeProvider = builder.mSizeProvider;
if (mSizeProvider == null) {
mSizeProvider = new SizeProvider() {
@Override
public int dividerSize(int position, RecyclerView parent) {
return DEFAULT_SIZE;
}
};
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
RecyclerView.Adapter adapter = parent.getAdapter();
if (adapter == null) {
return;
}
int itemCount = adapter.getItemCount();
int lastDividerOffset = getLastDividerOffset(parent);
int validChildCount = parent.getChildCount();
int lastChildPosition = -1;
for (int i = 0; i < validChildCount; i++) {
View child = parent.getChildAt(i);
int childPosition = parent.getChildAdapterPosition(child);
if (childPosition < lastChildPosition) {
// Avoid remaining divider when animation starts
continue;
}
lastChildPosition = childPosition;
if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
// Don't draw divider for last line if mShowLastDivider = false
continue;
}
if (wasDividerAlreadyDrawn(childPosition, parent)) {
// No need to draw divider again as it was drawn already by previous column
continue;
}
int groupIndex = getGroupIndex(childPosition, parent);
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
continue;
}
Rect bounds = getDividerBound(groupIndex, parent, child);
switch (mDividerType) {
case DRAWABLE:
Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
drawable.setBounds(bounds);
drawable.draw(c);
break;
case PAINT:
mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
break;
case COLOR:
mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
break;
}
}
}
@Override
public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(v);
int itemCount = parent.getAdapter().getItemCount();
int lastDividerOffset = getLastDividerOffset(parent);
if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
// Don't set item offset for last line if mShowLastDivider = false
return;
}
int groupIndex = getGroupIndex(position, parent);
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
return;
}
setItemOffsets(rect, groupIndex, parent);
}
/**
* Check if recyclerview is reverse layout
*
* @param parent RecyclerView
* @return true if recyclerview is reverse layout
*/
protected boolean isReverseLayout(RecyclerView parent) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) layoutManager).getReverseLayout();
} else {
return false;
}
}
/**
* In the case mShowLastDivider = false,
* Returns offset for how many views we don't have to draw a divider for,
* for LinearLayoutManager it is as simple as not drawing the last child divider,
* but for a GridLayoutManager it needs to take the span count for the last items into account
* until we use the span count configured for the grid.
*
* @param parent RecyclerView
* @return offset for how many views we don't have to draw a divider or 1 if its a
* LinearLayoutManager
*/
private int getLastDividerOffset(RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
int itemCount = parent.getAdapter().getItemCount();
for (int i = itemCount - 1; i >= 0; i--) {
if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
return itemCount - i;
}
}
}
return 1;
}
/**
* Determines whether divider was already drawn for the row the item is in,
* effectively only makes sense for a grid
*
* @param position current view position to draw divider
* @param parent RecyclerView
* @return true if the divider can be skipped as it is in the same row as the previous one.
*/
private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
}
return false;
}
/**
* Returns a group index for GridLayoutManager.
* for LinearLayoutManager, always returns position.
*
* @param position current view position to draw divider
* @param parent RecyclerView
* @return group index of items
*/
private int getGroupIndex(int position, RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
return spanSizeLookup.getSpanGroupIndex(position, spanCount);
}
return position;
}
protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
/**
* Interface for controlling divider visibility
*/
public interface VisibilityProvider {
/**
* Returns true if divider should be hidden.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return True if the divider at position should be hidden
*/
boolean shouldHideDivider(int position, RecyclerView parent);
}
/**
* Interface for controlling paint instance for divider drawing
*/
public interface PaintProvider {
/**
* Returns {@link android.graphics.Paint} for divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Paint instance
*/
Paint dividerPaint(int position, RecyclerView parent);
}
/**
* Interface for controlling divider color
*/
public interface ColorProvider {
/**
* Returns {@link android.graphics.Color} value of divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Color value
*/
int dividerColor(int position, RecyclerView parent);
}
/**
* Interface for controlling drawable object for divider drawing
*/
public interface DrawableProvider {
/**
* Returns drawable instance for divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Drawable instance
*/
Drawable drawableProvider(int position, RecyclerView parent);
}
/**
* Interface for controlling divider size
*/
public interface SizeProvider {
/**
* Returns size value of divider.
* Height for horizontal divider, width for vertical divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Size of divider
*/
int dividerSize(int position, RecyclerView parent);
}
public static class Builder<T extends Builder> {
private Context mContext;
protected Resources mResources;
private PaintProvider mPaintProvider;
private ColorProvider mColorProvider;
private DrawableProvider mDrawableProvider;
private SizeProvider mSizeProvider;
private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
@Override
public boolean shouldHideDivider(int position, RecyclerView parent) {
return false;
}
};
private boolean mShowLastDivider = false;
private boolean mPositionInsideItem = false;
public Builder(Context context) {
mContext = context;
mResources = context.getResources();
}
public T paint(final Paint paint) {
return paintProvider(new PaintProvider() {
@Override
public Paint dividerPaint(int position, RecyclerView parent) {
return paint;
}
});
}
public T paintProvider(PaintProvider provider) {
mPaintProvider = provider;
return (T) this;
}
public T color(final int color) {
return colorProvider(new ColorProvider() {
@Override
public int dividerColor(int position, RecyclerView parent) {
return color;
}
});
}
public T colorResId(@ColorRes int colorId) {
return color(ContextCompat.getColor(mContext, colorId));
}
public T colorProvider(ColorProvider provider) {
mColorProvider = provider;
return (T) this;
}
public T drawable(@DrawableRes int id) {
return drawable(ContextCompat.getDrawable(mContext, id));
}
public T drawable(final Drawable drawable) {
return drawableProvider(new DrawableProvider() {
@Override
public Drawable drawableProvider(int position, RecyclerView parent) {
return drawable;
}
});
}
public T drawableProvider(DrawableProvider provider) {
mDrawableProvider = provider;
return (T) this;
}
public T size(final int size) {
return sizeProvider(new SizeProvider() {
@Override
public int dividerSize(int position, RecyclerView parent) {
return size;
}
});
}
public T sizeResId(@DimenRes int sizeId) {
return size(mResources.getDimensionPixelSize(sizeId));
}
public T sizeProvider(SizeProvider provider) {
mSizeProvider = provider;
return (T) this;
}
public T visibilityProvider(VisibilityProvider provider) {
mVisibilityProvider = provider;
return (T) this;
}
public T showLastDivider() {
mShowLastDivider = true;
return (T) this;
}
public T positionInsideItem(boolean positionInsideItem) {
mPositionInsideItem = positionInsideItem;
return (T) this;
}
protected void checkBuilderParams() {
if (mPaintProvider != null) {
if (mColorProvider != null) {
throw new IllegalArgumentException(
"Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
}
if (mSizeProvider != null) {
throw new IllegalArgumentException(
"Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
}
}
}
}
}
上面是 LinearLayoutManager用的,用法如下:
HorizontalDividerItemDecoration divider = new HorizontalDividerItemDecoration.Builder(getActivity())
.drawable(R.drawable.custom_divider)
.size(10)
.build();
recyclerView.addItemDecoration(divider);
custom_divider.xml代码如下,就是分割线的样式:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--<gradient-->
<!--android:centerColor="#ff00ff00"-->
<!--android:endColor="#ff0000ff"-->
<!--android:startColor="#ffff0000" -->
<!--android:type="linear" />-->
<solid android:color="@color/app_main_gray_bg"></solid>
<size android:height="8dp" />
</shape>
以上就是 LinearLayoutManager分割线的用法以及代码,下面贴一个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);
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 = spacing;
}
}
}
用法如下:
rv_student_list.addItemDecoration(new GridSpacingItemDecoration(4, DensityUtil.dip2px(12),false));
第一个参数代表这个RecyclerView有几列?第二个参数代表分割线的宽度,单位是像素PX,第三个 includeEdge参数代表意思是否在边缘也添加分割线,即RecyclerView的最左边和最右边。
以上~~~~