近日有消息称:阿里巴巴集团与其关联公司蚂蚁金服,计划牵头对订餐平台饿了么投入至少10亿美元资金。因为随着越来越多的中国消费者开始使用智能手机订餐、预约家政服务,此类服务的市场规模在今年将有望达到人民币7.28万亿元。
本篇来自 frank909 的投稿,分享了ItemDecoration的基本用法以及进阶使用,希望能够帮助到大家。另外,大家不要忘了明天还要继续上班。
frank909 的博客地址:
http://blog.csdn.net/briblue
毫无疑问,RecyclerView 是现在 Android 世界中最重要的系统组件之一,它的出现就是为了高效代替 ListView 和 GridView。当时它的出现解决了我一个大的需求,这个需求就是在电视盒子界面上横向加载应用列表,由于 ListView 没有横向加载的功能,而网络上开源的那些 HorizontalListView 又不满足需求,所以我们只能自定义 ViewGroup 来实现需求,但是回收机制不是很完善,所以性能并不好,所以当 RecyclerView 横空出世时,我第一时间拥抱了它,并推荐 Android 开发小组成员们去了解它。
但后来,我发现 RecyclerView 除了比 ListView 好用外,某些地方它却更复杂了,它将更多的权力交给了开发者自己,比如布局,比如 ITEM 的分割线,比如点击监听等等。但总归它是好东西,所以我们得多花些时间来学习,平常开发我们一般按照 RecyclerView 的基本用法便可以实现绝大多数需求,但是某些场景下却远远不够,比如我们不想局限于 LinearLayoutManager 想自己定义 LayoutManager,我们需要定义时光轴的效果,我们想实现美妙的添加删除动画等等,这些情况下解决问题的话需要我们对 RecyclerView 本身有足够的了解。
今天,这篇文章不讲 RecyclerView 基本的知识和用法,讲它一个有趣的知识点 ItemDecoration。
Decoration 的英文意思是装饰物的意思,引申到这里来,肯定也是与 RecyclerView 的界面装饰有关。我们常见的就是分割线了。
我们在使用 ListView 的时候只要在 xml 文件中,使用 android:divider 就可以,但是很遗憾 RecyclerView 却没有相应的控制。
我们新建一个工程,然后在一个页面里面添加一个 RecyclerView。创建相关的 Adapter,加载布局文件,这里布局文件很简单,就是一个 TextView,再之后在 Activity 初始化它。
可以看到所有的选项都混在一起,为了美观应该需要 1px 的分割线,之前我一般在 Item 的布局文件中设置它的 topMargin 或者是 bottomMargin,所以我们可以在相关的 Adapter 中这样修改。
效果如下:
现在我们同样可以通过给 RecyclerView 添加 ItemDecoration 来实现它。
首先,我们需要自定义一个 ItemDecoration,按照目前的需求,我们只需要实现它的一个方法就可以了。
然后在 Activity 中添加它到 RecyclerView 就可以了。
效果跟上面是一样的。
getItemOffsets()
我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。
Rect outRect
View view
RecyclerView parent
RecyclerView.State state
这四个参数分别干什么的呢?我们不妨在 AndroidStudio 中按 Ctrl 键点击方法名,就可以到了它被调用的位置。
我们注意这一行代码 mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); 很容易得知,outRect 是一个全为 0 的 Rect。view 指 RecyclerView 中的 Item。parent 就是 RecyclerView 本身,state 就是一个状态。
我们可以看下面的这张图。
绿色区域代表 RecyclerView 中的一个 ItemView,而外面橙色区域也就是相应的 outRect,也就是 ItemView 与其它组件的偏移区域,等同于 margin 属性,通过复写 getItemOffsets() 方法,然后指定 outRect 中的 top、left、right、bottom 就可以控制各个方向的间隔了。注意的是这些属性都是偏移量,是指偏移 ItemView 各个方向的数值。
在上面的例子中我设置了 outRect.top = 1; 所以每个 ItemView 之间有 1 px 的空隙,而这 1 px 空隙透露了下面背景色,所以看起来就像是分隔线,这实现了简单的分隔线效果,但这种方法分隔线的效果只能取决于背景色,如果我要定制分割线的颜色呢?这个时候就要讲到一个新的方法名 onDraw()。
onDraw()
在 Android 中的每一个 View 中 onDraw() 是很重要的一个方法,用来绘制组件的UI效果,所以在 ItemDecocration 中它自然也是用来绘制外观的。我们来看它的方法声明。
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);
可以看到它传递了一个 Canvas 参数对象,所以它拥有了绘制的能力。但是怎么绘制呢?
其实它是配合了前面的 getItemOffsets 方法一起使用的,getItemOffsets 撑开了 ItemView 的上下左右间隔区域,而 onDraw 方法通过计算每个 ItemView 的坐标位置与它的 outRect 值来确定它要绘制内容的区间。
假设,我们要设计一个高度为 2 px 的红色分割线,那么我们就需要在每个 ItemView top位置上方画一个 2 px 高度的矩形,然后填充颜色为红色。
需要注意的一点是 getItemOffsets 是针对每一个 ItemView,而 onDraw 方法却是针对 RecyclerView 本身,所以在 onDraw 方法中需要遍历屏幕上可见的 ItemView,分别获取它们的位置信息,然后分别的绘制对应的分割线。
我们看下面的这张示意图:
为了便于观察我将第一条分割线的颜色透明化了,我们可以看到每条分割线绘制的区域其实就是 outRect.top 至 ItemView.top 之间的区域,所以我们就需要在当初 getOffsets 方法进行位置偏移时就记录下每个 itemView 向上的间隔距离,之后的逻辑就是遍历屏幕上的 View,然后描绘分割线。
然后我们在 Activity 将 ColorDividerItemDecoration 添加到相应的 RecyclerView 中就可以了。
mRecyclerView.addItemDecoration(new ColorDividerItemDecoration());
至此,红色的分割线就搞定了。
但一定要注意的是,onDraw 方法可不只能绘制简单的线条,它可是拥有 Canvas 的,所以画圆、画矩形、画弧形、绘制图片都不在话下。为了提高本篇代码的技术含量,下面我们通过 ItemDecoration 来实现一个时光轴的效果。
通过ItemDecoration实现时光轴的效果
编码的开始先做设计,或者说先思考。思考我们要做什么,或者说要怎么做。
我们可以看到左边白色的图案就大概是我们时光轴要绘制的图形。我们通过 getItemOffsets 方法来对 ItemView 进行 left 和 top 的间距设置。然后确定好轴线的起始坐标,中间轴结点的图形或者是图案。我们可以通过 ItemView 将相应的时光轴片断分解,如下图。
主要是一些参数的确定,例如 DividerHeight,注意这个 DividerHeight 不是指 ItemView 向上的间隔值,而是相应的 ItemDecoration 的高度。中心坐标 (centerX,centerY),还有上下两段轴线的起始坐标。有了这些参数后,我们就能轻松地编码了。
感觉不怎么美观,我们尝试将结点的实心圆改成空心圆。
感觉美观了许多。同时,我们可以将轴结点用图标代替圆或者圆圈。
上面的图标感觉更好看了。
需要注意的是 onDraw 方法,ItemDecoration 是在 ItemView 的下方绘制的,也就是 ItemView 可能会覆盖 ItemDecoration 的内容。我们可以验证一下,在时间轴与 ItemView 的边界画一个完整的圆,观察它的效果。
mPaint.setStyle(Paint.Style.STROKE);
c.drawCircle(view.getLeft(),centerY,mNodeRadius,mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
可以看到,在重合的地方,圆圈确实被 ItemView 内容覆盖了。
大家可能会想到,ItemDecoration 内容能不能覆盖在 ItemView 内容之上呢?
答案是肯定的,但不是在 onDraw() 方法实现,而是另外一个方法 onDrawOver()。
onDrawOver和角标
现实中的APP或者网站经常有一些排行榜比如下面:
或者这样:
这些角标都是绘制在 ItemView 之上的,现在有了 ItemDecoration 我们也可以轻松而优雅地实现它。
比如我们要实现一个图书销量排行榜。我们有大概的草图。
然后我们就可以编码了,这里我们主要看下自定义 FlagItemDecoration:
最终效果如下图:
有人在想,通过 ItemView 中的布局文件不就可以完成这样的操作吗?是的,确实是可以的,将 Flag 角标定义在每一个 ItemView 布局文件中,然后在 Adapter 的 onBindViewHolder 方法中根据 postion 的值来决定是否加载角标。
但是这里是为了说明 ItemDecoration 中的 onDrawOver 方法,为了说明它确实能让 ItemDecoration 图像绘制在 ItemView 内容之上。事实上,ItemDecoration 的妙处还有好多好多。
自定义一个 ItemDecoration 通常要根据需要,复写它的 3 个方法。
getItemOffsets 撑开 ItemView 上、下、左、右四个方向的空间
onDraw 在 ItemView 内容之下绘制图形
onDrawOver 在 ItemView 内容之上绘制图形。
完整源码地址:
http://download.csdn.net/detail/briblue/9813393
github地址
https://github.com/frank909zhao/ItemDecorationDemo
每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。
如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。
欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号: