泛览天下

阅读,看尽天下事

1.Android recycleView万能分隔线 GridLayoutManager布局item左右间距均等(最易懂)

2022-08-04 00:04:20


判断是否是第一个,最后一个,是单个还是双个,是什么类型/*** * 分割线要注意,没有颜色,默认是白色的,会看不出来 * @param outRect * @param view * @param parent * @param state


从月薪3000到年薪60万。从专科生到深圳一线大厂。关注我就能达到大师级水平,这话我终于敢说了, 年薪60万不是梦!

今天开始讲RecycleView的系列教程。分割线,分组,局部刷新,动态添加,缓存原理,抖音效果,瀑布流。嵌套,动画等等


RecyclerView的分割线是通过canvas和设置item偏移画出来的.需要知道2个方法

getItemOffsets()和onDraw方法

getItemOffsets 是针对每一个 ItemView

onDraw:遍历,进行颜色修改

我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。

Rect outRect

View view

RecyclerView parent

RecyclerView.State state

绿色区域代表 RecyclerView 中的一个 ItemView,而外面橙色区域也就是相应的 outRect,也就是 ItemView 与其它组件的偏移区域,等同于 margin 属性,通过复写 getItemOffsets() 方法,然后指定 outRect 中的 top、left、right、bottom 就可以控制各个方向的间隔了。

这实现了简单的分隔线效果,但这种方法分隔线的效果只能取决于背景色,如果我要定制分割线的颜色呢?这个时候就要 onDraw()。

————————————————

源码分析:在recycleview中的

分割线要注意,没有颜色,默认是白色的,会看不出来

第一种方案,通过

getItemOffsets()方法进行分割线!

判断是否是第一个,最后一个,是单个还是双个,是什么类型

/***

* 分割线要注意,没有颜色,默认是白色的,会看不出来

* @param outRect

* @param view

* @param parent

* @param state

*/

private void testItemOffset(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

int childAdapterPosition = parent.getChildAdapterPosition(view);

if (childAdapterPosition ==0) {

outRect.set(0, 20, 0, 20);

}else {

outRect.set(0, 0, 0, 20);

}

}

第二种方案:ondraw()

@Override

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

super.onDraw(c, parent, state);

int childCount = parent.getChildCount();

for (int i =0; i < childCount; i++) {

View view = parent.getChildAt(i);

int index = parent.getChildAdapterPosition(view);

//第一个ItemView不需要绘制

if (index ==0) {

continue;

}

float dividerTop = view.getTop() -mDividerHeight;

float dividerLeft = parent.getPaddingLeft();

float dividerBottom = view.getTop();

float dividerRight = parent.getWidth() - parent.getPaddingRight();

c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);

}

}

GridLayoutManager布局item左右间距均等

思路分析

首先,我们知道,对于 GridLayoutmanager ,当我们设置的 spancount 为 3 的时候,那么每个 item 的最大宽度为 itemMaxW = recycylerW / spancount = recycylerW / 3.

假设我们 spancount 为 3,那么在不设置 itemDercation 的情况下它的分布是这样的,可以看到第一列与最后一行的距离是不一样的

GridVIew出现的问题:本来固定item.高度和宽度

1.分割线有,不是理想的,左右均等

2.上下没有分割线


源码得到:

按上面分析的源码,我们可以知道,调用outRect.set(int left, int top, int right, int bottom)方法时,left一直为0,right一直为divider的宽度,而每一项item的宽度都要减去(left+right)大小,

left一直为0,right一直为divider的宽度

左上右下到底是什么的值?

计算每一个item移动的距离,左边和右边的移动距离

计算分析:

1.左边的分割线宽度为sW  (已知)

2.每个显示item的宽度,布局定义的itemWidth

3. 总共分割线宽度:totalDivider=屏幕宽度-spanCount*itemWidth

4.列之间的分割线宽度为dw   =(屏幕宽度-spanCount*item-2*sW )/(spantcount-1)

5.每个item需要留出的空间 ew=totalDivider/spanCount(即paddingLeft+paddingRight)

left:  左边的间距值(绝对值,差值)

right:右边的间距值

每个item移动的距离:

第一个Item:L0=sW                                  R0=eW-sW

第二个Item:L1=dW-R0=dW-eW+sW       R1=eW-L1=2eW-dW-sW

第三个Item:L2=dW-R1=2(dW-eW)+sW   R2=eW-L2=3eW-2dW-sW

得出公式:

Ln=(position%spanCount)*(dw-ew)+sw

Rn=ew-Ln

总结:得到3个值dw,ew, sw的值

sw:左边的距离

ew:每个的平均的分割线

dw: 列之间的分割线宽度

int firstLastSpace =50;//最左边的分割线宽度

@SuppressLint("LongLogTag")

@Override

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

super.getItemOffsets(outRect, view, parent, state);

count++;

outRect.top =20;

mDividerHeight =0;

int itemWidth =dip2px(context, 100);

int screenWidth = getScreenWidth(context);

int dw = (screenWidth -3 * itemWidth -2 *firstLastSpace) /2;//最终计算出这个padding值

//误区:中间的分割线的总距离,左右可能是不等的

int totalDivder = screenWidth -3 * itemWidth;

Log.d("TestDividerItemDecoration", "totalDivder" + totalDivder);

int eachDivder = totalDivder /3;

int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();

//不要用for循环

outRect.left = (itemPosition %3) * (dw - eachDivder) +firstLastSpace;

outRect.right = eachDivder - outRect.right;

}

错误的思路:

//误区:中间的分割线的总距离,左右可能是不等的

//不要用for循环

int firstLastSpace =50;//最左边的分割线宽度

@SuppressLint("LongLogTag")

@Override

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

super.getItemOffsets(outRect, view, parent, state);

count++;

outRect.top =20;

mDividerHeight =0;

int itemWidth =dip2px(context, 100);

int screenWidth = getScreenWidth(context);

int padding = (screenWidth -3 * itemWidth -2 *firstLastSpace) /4;//最终计算出这个padding值

//不能这么算,必须保证每个item的分割线一样才行。

Log.d("TestDividerItemDecoration", "getItemOffsets" +count +"item宽度:" + itemWidth +"padding" + padding);

//仅仅计算左边和右边的距离

int childCount = parent.getChildCount();

for (int i =0; i < childCount; i++) {

if (i %3 ==0) {//最左边的item

outRect.left =firstLastSpace;

outRect.right = padding;

}else if (i %3 ==1) {

outRect.left = padding;

outRect.right = padding;

}else if (i %3 ==2) {

outRect.left = padding;

outRect.right =firstLastSpace;

}

}

}

瀑布流的设置:

int spanIndex = layoutParams.getSpanIndex();

public class FeedDecorationextends RecyclerView.ItemDecoration {

private HomePageCardAdaptermHomePageCardAdapter;

public FeedDecoration(HomePageCardAdapter mHomePageCardAdapter) {

this.mHomePageCardAdapter = mHomePageCardAdapter;

}

@Override

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {

if (mHomePageCardAdapter ==null) {

return;

}

if (mHomePageCardAdapter.getItemViewType(parent.getChildAdapterPosition(view)) == HomePageMultipleCard.HOMEPAGE_MULTIPLE_CARD_TYPE_FITNESS_FEED) {

StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();

int spanIndex = layoutParams.getSpanIndex();

if (spanIndex ==0) {

outRect.set(DensityUtil.dip2px(ShadowApp.context(), 14), 0, DensityUtil.dip2px(ShadowApp.context(), 5), DensityUtil.dip2px(ShadowApp.context(), 10));

}else {

outRect.set(DensityUtil.dip2px(ShadowApp.context(), 5), 0, DensityUtil.dip2px(ShadowApp.context(), 14), DensityUtil.dip2px(ShadowApp.context(), 10));

}

}

}

}


demo地址: https://github.com/pengcaihua123456/shennandadao