最近在使用同事寫的GridSpaceItemDecoratoin時發(fā)現(xiàn)不太好用,準備自己重新寫一個,于是在網(wǎng)上找資源
在Google找了不少資料,并不如意,故寫下此篇
以這位大神的文章為藍本,地址如下
Android RecyclerView 使用完全解析 體驗藝術般的控件
大家可以先看一下,ps:個人不太習慣把別人寫的很好的東西重新寫一篇,直接引用了
先聲明,文章并無意冒犯這位大神,僅想分享一下自己的方案,望共勉
文章的豎向列表的divider是沒有問題的,但是網(wǎng)格布局的divider使用,是有問題,讓我們先看一下Item偏移代碼
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent)
{
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,則不需要繪制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,則不需要繪制右邊
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
代碼邏輯是:最后一行不做底部偏移,最后一列不做右邊偏移,其它都做右邊和底部偏移,由此,最后一列的Item的寬度會比其它列要大Divider的尺寸
誤處分析
- 一個spanCount列的列表,加上divider后,一行Item減少的寬度為 (divider.size*spanCount-1)
- 而代碼中把這一行所減少的寬度平攤在0 - (spanCount-2)位置的Item上,最后一列未做減少,故導致最后一列Item變大
解決方案邏輯
- 一個spanCount列的列表,加上divider后,一行Item減少的寬度為 dividerSize*(spanCount-1)
- 則每個Item需減少 dividerSize*(spanCount-1)/spanCount
- 相鄰Item間距必須 是 dividerSize
- 第一列Item的左邊和最后一列Item的右邊偏移必須是0
- 依以上3條得出左右偏移量公式
- left = column * dividerSize / spanCount;
- right = dividerSize - (column + 1) * dividerSize / spanCount;
- ps: column 為Item在該行的位置
update verion 1.1
- 添加isHeadDivider控制headview是否顯示divider
- 刪除最右側邊線
- 適用于GridLayoutManager
- 用于StaggeredGridLayoutManager時,由于Item顯示位置不受控制,divider請適用背景顏色,有時間我會上圖片說明
附上完整代碼
package com.isenba.lightempty.support.view.customRecycleView;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.ColorRes;
import android.support.annotation.Px;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
/**
* Created by Mark on 2017/6/2.
*/
public class GridItemDecoration extends ItemDecoration {
Paint mVerPaint, mHorPaint;
Builder mBuilder;
public GridItemDecoration(Builder builder) {
init(builder);
}
void init(Builder builder) {
this.mBuilder = builder;
mVerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mVerPaint.setStyle(Paint.Style.FILL);
mVerPaint.setColor(builder.verColor);
mHorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHorPaint.setStyle(Paint.Style.FILL);
mHorPaint.setColor(builder.horColor);
}
private void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
//is draw head divider
//version 1.1 update
if(parent.getChildViewHolder(child).getItemViewType() == 1&&!mBuilder.isShowHeadDivider)
continue;
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin;
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mBuilder.dividerHorSize;
c.drawRect(left, top, right, bottom, mHorPaint);
}
}
private void drawVertical(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
//remove the rightmost divider
//verion 1.1 update
if((parent.getChildViewHolder(child).getAdapterPosition())%getSpanCount(parent)==0)
continue;
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin+mBuilder.dividerHorSize;
final int left = child.getRight() + params.rightMargin;
final int right = left + mBuilder.dividerVerSize;
c.drawRect(left, top, right, bottom, mVerPaint);
}
}
private int getSpanCount(RecyclerView parent) {
// 列數(shù)
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且縱向滾動
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且橫向滾動
{
if ((pos + 1) % spanCount == 0) {
return true;
}
}
}
return false;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
drawHorizontal(c, parent);
drawVertical(c, parent);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
if (mBuilder.isExistHeadView)
itemPosition -= 1;
//set head divider offset
//version 1.1 update
if(mBuilder.isShowHeadDivider&&itemPosition==-1)
outRect.set(0, 0, 0, mBuilder.dividerHorSize);
if (itemPosition < 0)
return;
int column = itemPosition % spanCount;
int bottom = 0;
int left = column * mBuilder.dividerVerSize / spanCount;
int right = mBuilder.dividerVerSize - (column + 1) * mBuilder.dividerVerSize / spanCount;
if (!(isLastRaw(parent, itemPosition, spanCount, childCount) && !mBuilder.isShowLastDivider))
bottom = mBuilder.dividerHorSize;
outRect.set(left, 0, right, bottom);
marginOffsets(outRect, spanCount, itemPosition);
}
private void marginOffsets(Rect outRect, int spanCount, int itemPosition) {
if (mBuilder.marginRight == 0 && mBuilder.marginLeft == 0)
return;
int itemShrink = (mBuilder.marginLeft + mBuilder.marginRight) / spanCount;
outRect.left += (mBuilder.marginLeft - (itemPosition % spanCount) * itemShrink);
outRect.right += ((itemPosition % spanCount) + 1) * itemShrink - mBuilder.marginLeft;
}
public static class Builder {
private Context c;
int horColor;
int verColor;
int dividerHorSize;
int dividerVerSize;
int marginLeft, marginRight;
boolean isShowLastDivider = false;
boolean isExistHeadView = false;
boolean isShowHeadDivider = false;
public Builder(Context c) {
this.c = c;
}
/**
* 設置divider的顏色
*
* @param color
* @return
*/
public Builder color(@ColorRes int color) {
this.horColor = c.getResources().getColor(color);
this.verColor = c.getResources().getColor(color);
return this;
}
/**
* 單獨設置橫向divider的顏色
*
* @param horColor
* @return
*/
public Builder horColor(@ColorRes int horColor) {
this.horColor = c.getResources().getColor(horColor);
return this;
}
/**
* 單獨設置縱向divider的顏色
*
* @param verColor
* @return
*/
public Builder verColor(@ColorRes int verColor) {
this.verColor = c.getResources().getColor(verColor);
return this;
}
/**
* 設置divider的寬度
*
* @param size
* @return
*/
public Builder size(@Px int size) {
this.dividerHorSize = size;
this.dividerVerSize = size;
return this;
}
/**
* 設置橫向divider的寬度
*
* @param horSize
* @return
*/
public Builder horSize(@Px int horSize) {
this.dividerHorSize = horSize;
return this;
}
/**
* 設置縱向divider的寬度
*
* @param verSize
* @return
*/
public Builder verSize(@Px int verSize) {
this.dividerVerSize = verSize;
return this;
}
/**
* 設置剔除HeadView的RecyclerView左右兩邊的外間距
*
* @param marginLeft
* @param marginRight
* @return
*/
public Builder margin(@Px int marginLeft,@Px int marginRight) {
this.marginLeft = marginLeft;
this.marginRight = marginRight;
return this;
}
/**
* 最后一行divider是否需要顯示
*
* @param isShow
* @return
*/
public Builder showLastDivider(boolean isShow) {
this.isShowLastDivider = isShow;
return this;
}
/**
* HeadView行divider是否需要顯示
* Version 1.1 add
*
* @param isShow
* @return
*/
public Builder showHeadDivider(boolean isShow){
this.isShowHeadDivider = isShow;
return this;
}
/**
* 是否包含HeadView
*
* @param isExistHead
* @return
*/
public Builder isExistHead(boolean isExistHead) {
this.isExistHeadView = isExistHead;
return this;
}
public GridItemDecoration build() {
return new GridItemDecoration(this);
}
}
}
如有問題 歡迎指正