理解RecyclerView的RecyclerView.ItemDecoration(二)
来源:互联网 发布:淘宝达人直播申请 编辑:程序博客网 时间:2024/04/25 08:38
上次我写了 理解RecyclerView的RecyclerView.ItemDecoration(一),介绍了ItemDecoration的第一个设置它padding的getItemOffsets方法,今天我们就来了解一下它的第二个方法onDraw()。这个方法主要是给每一个RecyclerView的item做一个装饰,这个装饰我们可以理解为很多种,其中一种最简单的就是画divider,即分割线。 还有,这个方法的执行时间在RecyclerView绘制每一个item之前,那么它所绘制的view在RecyclerView的item层的下面。
为了讲清楚自己所理解的ItemDecoration,大家主要注意转换一个观点,而这个观点是对比ListView的。那么还是从ListView的分割线开始吧。我们知道ListView的分割线是ListView的一个属性值就可以决定的:
<ListView android:id="@+id/list_view" android:divider="@color/colorPrimary" android:dividerHeight="2dp" android:layout_width="match_parent" android:layout_height="match_parent"/>
代码中divider和dividerHeight就可以决定我们的分割线的样式和大小了。但是RecyclerView却没有这个功能,原因很容易去想,因为RecyclerView可以配置不同的自定义的LayoutManager,不同的LayoutManager控制了RecyclerView中每一个Item的排列方式,不同的排列方式就意味着不同的divider排列方式和逻辑,这样对于RecyclerView来说是灾难性的,为了解决这个问题,所以RecyclerView就引出了ItemDecoration,而RecyclerView的中每一个item都拥有了自己的ItemDecoration,通过设置假设的padding值,去绘制自己的周围的东西,然后完成想要的结果。我们可以来看看下图:
在ListView的时代里,这种效果是很难实现的,因为那时候的divider都是一致的,执行的是同一个标准,但是当今时代耳朵RecyclerView是很容易实现的,原因是它把装饰每一个Item的任务都放开了,不管了,交给了ItemDecoration来打理了,通过ItemDecoration很容易实现这个效果了。
所以我们需要转变的角度就是,对于像分割线这样的任务,RecyclerView不是像ListView那样,把所有的样式和统一掉了,而是采取了一种放任不管的态度,对于每一个item,自己想怎么画就怎么画,充分展示了RecyclerView的多样性和高度的解耦性。这也就是ItemDecoration类的名字如此贴切的原因了,意为条目装饰了。
既然我们了解了ItemDecoration的本质,那么给它画个分割线是非常容易事了,还是来画个图分析一下吧:
对于一个竖直的LinearLayoutManager的LayoutManager,假设它的排列方式是vertical的,那么他们就是一直从上到下的排列,此时如果我想需要一个divider,那么首先需要设置一下这个:
@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.bottom = mDecorationHeight;}
先设置一下outRect的bottom值,这个不是很了解的,请看上一篇文章,那么就意味着我们每一个item底部就有了一个mDecorationHeight的padding值了,现在的我们,就需要在这个item底部的padding区域里面上色了,那么这个上色的过程就是今天需要讲的onDraw方法了:
@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { }
那么对于每一个item来讲,现在最重要的是找到bottompadding的区域,然后使用我们的Paint进行绘制了:
可以看出,我们需要知道绿色矩形的坐标值,知道坐标值我们就能对这个矩形添加颜色了,我们可以遍历RecyclerView的每一个View,通过View的位置来计算Rect的坐标值,这个很简单,相信大家看图就知道了,我就列一下代码大家观摩一下:
private void drawVertical(Canvas c, RecyclerView parent) { //对于最后一个item我们不需要进行绘制 int childCount = parent.getChildCount() - 1; Rect rect = new Rect(); for (int i = 0; i < childCount; i++) { View childView = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams(); //矩形的left点与RecyclerView的paddingleft()一致 rect.left = parent.getPaddingLeft(); rect.right = parent.getMeasuredWidth() - parent.getPaddingRight(); //这个就不说了,大家用心体会 rect.top = childView.getBottom() + params.bottomMargin; rect.bottom = rect.top + mDecorationHeight; //设置不同的颜色 mPaint.setColor(mColors[mRandom.nextInt(mColors.length)]); c.drawRect(rect, mPaint); } }
基本上得到的结果就是图一了,这个就没啥好说的啊。对于LinearLayoutManager.Horizontal,看下图估计也可以进行写出相应代码了:
基本代码如下,我也不多说了,思路大致一样。
private void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount() - 1; Rect rect = new Rect(); for (int i = 0; i < childCount; i++) { View childView = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams(); rect.left = childView.getRight() + params.rightMargin; rect.right = rect.left + mDecorationHeight; rect.top = parent.getPaddingTop(); rect.bottom = rect.top + childView.getMeasuredHeight() + params.bottomMargin; mPaint.setColor(mColors[mRandom.nextInt(mColors.length)]); c.drawRect(rect, mPaint); } }
好了,现在到了一个最难的,为StaggeredGridLayoutManager设置StaggeredGridLayoutItemDecoration了,为啥说这是最难的呢,待我我们慢慢去看。可能对StaggeredGridLayout的认识度不是很高,很多东西都不是太理解。
我们最终需要完成的样子如下:
对于StaggeredGridLayoutManager,我们所感觉的最多的也就是瀑布流了,好多照片墙的设置就是瀑布流的形式,图片的瀑布流的形成,是我们设置了图片的不同的高度,造成了视觉上的错觉,然后发现了美,好了,扯远了,现在来分析分析这玩意怎么加padding吧,然后才能画我们的Paint了。
我们先来声明一下,所有的item都是左边和下边加上padding,排在第一排的item上边加上paddingTop值,在最右边的加上item加上paddingRight值,说了这么多,大伙看图:
看好了,这就是我们的设置padding的地方,那么代码如下:
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { //先获取spanCount值 if (mSpanCount < 0) { StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) parent.getLayoutManager(); mSpanCount = manager.getSpanCount(); } //获取位置 outRect.left = mItemWidth; outRect.bottom = mItemWidth; int position = parent.getChildLayoutPosition(view); // 位于第一排的 需要来个paddingTop if (position < mSpanCount) { outRect.top = mItemWidth; } //位于最右边的加上paddingRight if((position +1 )% mSpanCount == 0) { outRect.right = mItemWidth; } }
这样对么??当然了,要是这么简单,我就是不认为它是坑了,原因是不对的,我们看看运行的结果:
我们看到了第2,5,8,11,14项出现了paddingRight,但是它并不是位于最右边的位置,这样就坑爹了,为啥呢??其实这需要从StaggeredGridLayoutManager的对item的排列方式说起了,它的layout并不是我们所想象的那样,感觉就是简单的顺序排列,而是一种非常复杂的方式进行layout的。怎么说呢?咋们来用图描述描述:
图中有了5个item进行插入了,但是它们的排列顺序并不是我们想象的那样,感觉item3需要排列在第0列,但是结果它却排在了第2列,item4需要排列在第1列,却现在排在了第2列。这个问题深究起来需要对StaggeredGridLayoutManager比较熟悉,我还是比较梗,但是我还是想说一下,因为我也不是很懂它的源码。还记得我们初始化StaggeredGridLayoutManager时传入了一个参数吗?
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
这里传入了一个3,当然你可能觉得这个3就是告诉StaggeredGridLayoutManager我就需要三列,当然要是这么简单就很容易了,当你传入了一个3之后,StaggeredGridLayoutManager内部就会初始化三个Span类来记录每一列的高度,当每一个span类都有数值时,也就是说明它到了第二行,此时就需要比较三个span中哪一个数值是最小的,如果是最小的,我就把下一个view插入到这一列下,然后再增加该列下的span值的高度。理解了这个,你就会发现下面的公式是计算的不准确的,所以需要改啊,怎么改呢??
//位于最右边的加上paddingRightif((position +1 )% mSpanCount == 0) { outRect.right = mItemWidth;}
幸好,stackoverflow上早有大神给出了答案,获取每一个view上的spanIndex,通过spanIndex来判断View是否在最右边。正因为这个,我还修复了github上一个给出的时间线Dome, 如果你有时间,可以去瞅瞅,顺便给个star.
//位于最右边的加上rightStaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); int spanIndex = params.getSpanIndex(); //当然了,这个还是需要些技巧的啊。 if (spanIndex == mSpanCount - 1) { outRect.right = mItemWidth; }
好了,我们解决了这个问题之后,现在就需要draw我们的padding值了,这个也基本上和LinearLayoutManager上差不多,只不过需要 drawVertical和drawHorizontal了,也差不多了吧,稍后代码会打包上来的,别急。
写到这里感觉也差不多,虽然写了很多废话,但是锻炼一下自己的认知能力,还是有必要的。好了,这是第二篇介绍这玩意的,就写到这里了吧。
代码
- 理解RecyclerView的RecyclerView.ItemDecoration(二)
- 理解RecyclerView的RecyclerView.ItemDecoration(一)
- RecyclerView.ItemDecoration的使用
- 关于RecyclerView的ItemDecoration
- RecyclerView.ItemDecoration(笔记)
- Android RecyclerView <二> fragment+ItemDecoration
- RecyclerView.ItemDecoration
- RecyclerView的分割线 - ItemDecoration
- RecyclerView的分割线:ItemDecoration
- RecyclerView 的 ItemDecoration 学习纪录
- 深入理解 RecyclerView 系列之一:ItemDecoration
- 深入理解 RecyclerView 系列之一:ItemDecoration
- 使用RecyclerView的ItemDecoration实现StickyHeader效果(笔记)
- 【Android】RecyclerView的分割线ItemDecoration
- Android RecyclerView 的 网格布局 ItemDecoration
- RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现
- 自定义 RecyclerView.ItemDecoration
- RecyclerView.ItemDecoration 使用总结
- win10 安全模式开机
- java 标签中图片加载不完全问题
- java中==和equals的区别
- 文件操作-打开、读写
- Android串口通信:串口读写实例
- 理解RecyclerView的RecyclerView.ItemDecoration(二)
- 一起艳恶学习开发遇到的坑(一)
- 仿淘宝一键置顶的判断及其详细实现
- ajax跨域问题以及解决方案
- java集合框架之Vector
- 5-4 List Leaves
- 简单理解树状数组(单个数据)
- 软键盘隐藏和弹出的监听
- 哈夫曼编码与解码的JavaScript实现