专栏名称: 郭霖
Android技术分享平台,每天都有优质技术文章推送。你还可以向公众号投稿,将自己总结的技术心得分享给大家。
目录
相关文章推荐
郭霖  ·  HarmonyOS ... ·  5 天前  
郭霖  ·  这一次,让EventBus纯粹一些 ·  3 天前  
鸿洋  ·  大图检测插件的落地 ·  3 天前  
鸿洋  ·  初识Android内存优化 ·  4 天前  
鸿洋  ·  打造Android热修复专家 ·  6 天前  
51好读  ›  专栏  ›  郭霖

Android仿微信通讯录:悬停头部分组列表

郭霖  · 公众号  · android  · 2016-10-11 08:00

正文

今日科技快讯

昨日,vivo与NBA在上海举办了战略发布会,宣布vivo将成为NBA在中国唯一的手机市场官方合作伙伴。未来多年里,双方将在赛事、产品、服务以及品牌营销等方面展开全方位战略合作。

根据内部人士透露,vivo将会推出一系列NBA主题的深度定制手机以及周边产品。同时,在未来多年中参与NBA国际系列赛中国站、NBA球迷答谢之夜等一系列品牌活动。此外,vivo还将推出“vivo校园篮球计划”、“vivo走进赛场”等一系列品牌定制的NBA主题活动。

作者简介

本篇来自 张旭童 的投稿,分享了如何使用 ItemDecoration 实现 悬停头部分组列表功能,这种实现方式还是很简便的,希望能帮助有需要的朋友。

张旭童 的博客地址:

http://blog.csdn.net/zxt0601

概述

效果如下,两个ItemDecoration,一个实现悬停头部分组列表功能,一个实现分割线:

网上关于实现带悬停分组头部的列表的方法有很多,像我看过有主席的自定义 ExpandListView 实现的,也看过有人用一个额外的父布局里面套 RecyclerView/ListView + 一个头部View(位置固定在父布局上方)实现的。

对于以上解决方案,有以下几点个人觉得不好的地方: 

1. 现在 RecyclerView 是主流。

2.RecyclerView 外套一个父布局总归是增加布局层级,容易overdraw,显得不够优雅。 

3. item布局 实现带这种分类头部的方法有两种,一种是把分类头部当做一种itemViewtype(麻烦),另一种是每个 Item布局 都包含了分类头部的布局,代码里根据postion 等信息动态 Visible,Gone头部(布局冗余,item效率降低)。

况且Google为我们提供了 ItemDecoration,它本身就是用来修饰 RecyclerView 里的Item 的,它的 getItemOffsets()onDraw() 方法用于为Item分类头部留出空间和绘制(解决缺点3),它的 onDrawOver() 方法用于绘制悬停的头部View(解决缺点2)。 

而且更重要的是,ItemDecoration 出来这么久了,你还不用它?

本文就利用 ItemDecoration 打造 分组列表,并配有悬停头部功能。

亮点预览:添加多个ItemDecoration、它们的执行顺序、ItemDecoration方法执行顺序、ItemDecoration和RecyclerView的绘制顺序

使用ItemDecoration

用法:为 RecyclerViewPool 添加 一个或多个 ItemDecoration

RecyclerView 添加 ItemDecoration 只要这么一句 addItemDecoration()

它有两个同名重载方法: 

  • addItemDecoration(ItemDecoration decor) 常用,(按照add顺序,依次渲染ItemDecoration)

  • addItemDecoration(ItemDecoration decor, int index) add一个ItemDecoration,并为它指定顺序

上来就高能,别的讲解 RecyclerView 的文章一般都是对 ItemDecoration 一笔带过,用的Demo 一般也都是官方的 DividerItemDecoration类,更别提还添加多个 ItemDecoration了。其实我也是写Demo的时候才发现这个方法,点进去查看了一下源码:

老套路:我们最常用的单参数方法 内部调用了双参数方法,并把index 传入-1

我们 add 的 ItemDecoration 都存储在 RecyclerView 类的 mItemDecorations 变量里, 这个变量就是一个ArrayList,定义如下:

private final ArrayList mItemDecorations = new ArrayList();

ItemDecoratuon方法介绍

常用(全部)方法,按照在 RecyclerView 中它们被调用的顺序排列:

这个三个方法也是继承一个 ItemDecoration 必须实现的三个方法。(其实 ItemDecoration 里除了@Deprecated 的方法 也就它们三了)


方法一的编写


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

我们需要利用 parent 和 state 变量,来获取需要的辅助信息,例如 postion, 最终调用 outRect.set(int left, int top, int right, int bottom)方法,设置四个方向上需要为 ItemView 设置 padding 的值。

下图我觉得很经典:摘自(http://blog.piasy.com/2016/03/26/Insight-Android-RecyclerView-ItemDecoration/?utm_source=tuicool&utm_medium=referral):

本文的 实体bean 如下编写:

getItemOffsets 方法 如下:

通过 parent 获取 postion 信息,通过 postion 拿到数据里的每个bean里的分类,因为数据集已经有序,如果与前一个分类不一样,说明是一个新的分类,则需要绘制头部outRect.set(0, mTitleHeight, 0, 0),否则不需要 outRect.set(0, 0, 0, 0)。


方法二的编写


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

我们需要利用 parent state 变量,来获取需要的辅助信息,例如绘制的上下左右,childCount, childView 等。最终利用 Canvas c 调用 Canvas 的方法来绘制出我们想要的UI。会自定义View就会写本方法。

onDraw 绘制出的内容是在 ItemView 下层,虽然它可以绘制超出 getItemOffsets() 里的Rect区域,但是超出区域最终不会显示,但被 ItemView 覆盖的区域会产生 OverDraw。

本文如下编写:通过parent获取绘制UI的 left 和 right 以及 childCount, 遍历 childView,根据 childView 的 postion,和方法一中的判断方法一样,来决定是否绘制分类Title区域。

分类绘制 title 的方法就是 自定义View 的套路,根据确定的上下左右范围先 drawRect 绘制一个背景,然后 drawText 绘制文字。

写完方法1、2,就已经完成了分类列表title的绘制,方法3实现顶部悬停title效果。


方法三的编写


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

onDraw() 方法类似, 我们需要利用 parent 和 state 变量,来获取需要的辅助信息,例如绘制的上下左右,position, childView等。最终利用 Canvas c 调用 Canvas 的方法来绘制出我们想要的UI。同样是会自定义View就会写本方法。

onDrawOver 绘制出的内容是在 RecyclerView 的最上层,会遮挡住 ItemView,So天生自带悬停效果,用来绘制悬停View再好不过。

本文如下编写:首先通过 parent 获取 LayoutManager(由于悬停分组列表的特殊性,写死了是 LinearLayoutManger),然后获取当前第一个可见 itemView 以及 postion,以及它所属的分类 title(tag),然后绘制悬停View的背景和文字(tag),可参考方法2里的书写,大同小异。

至此,我们的 带悬停头部的分组列表 ItemDecoration 就编写完毕了。

一些ItemDecoration补充

一.多个ItemDecoration,以及它们的绘制顺序。

就像上面用法提到的,可以为一个 RecyclerView 添加 多个ItemDecoration,那么 多个ItemDecoration 的绘制顺序是什么呢?

多个ItemDecoration 最终是存储在 RecyclerView 里的 mItemDecorations(ArrayList) 变量中,那我们就去 RecyclerView 的源码里搜一搜,看看哪些地方用到了 mItemDecorations

发现在 draw() onDraw() 方法里:按照在 mItemDecorations 里的 postion顺序,依次调用了 每个 ItemDecoration onDrawOver onDraw 方法。所以 后添加的ItemDecoration,如果和 前面的ItemDecoration 的绘制区域有重合的地方,会遮盖住前面的ItemDecoration(OverDraw)。

二. ItemDecoration和RecyclerView的Item的绘制顺序

在介绍ItemDecoration的三个方法时,我们提到过结论:

  • ItemDecoration 的 onDraw 最先调用,绘制在最底层;

  • 其上再绘制 ItemView 中间层;

  • 再上调用 ItemDecoration 的 onDrawOver,绘制在最上层。

理由: 由上面代码可见, RecyclerView 的 draw() 方法中,在 super.draw(c) 方法调用完后,才调用 mItemDecorations.get(i).onDrawOver(c, this, mState);

super.draw(c) 方法就是直接调用 View 的 public void draw(Canvas canvas) 方法(如下图所示) 其中又先调用了 View(RecyclerView)onDraw() 方法;

RecyclerView onDraw() 方法中,会调用 mItemDecorations.get(i).onDraw(c, this, mState);

所以onDraw最先调用,绘制在最底层

后调用了 View(ViewGroup) dispatchDraw(canvas) 方法,会执行 drawChild(Canvas canvas, View child, long drawingTime) 方法,绘制每个 itemView。

所以ItemView绘制在中间层

最后 super.draw(c) 走完,调用 mItemDecorations.get(i).onDrawOver(c, this, mState);

所以 再调用 ItemDecoration 的 onDrawOver,绘制在最上层。(从方法名字也可以看出哈)

View 的 draw() 方法如下:

总结

RecyclerView相关的各个类,个个是宝,每一次探索都觉得如获至宝, 感觉利用ItemDecoration可以干很多事,可惜ItemDecoration貌似不能接受到用户的点击事件~要不我右侧导航栏都想用ItemDecoration实现了。

关于可以 add 多个ItemDecoration 这一点,想了一下,觉得很精妙,这是一种很好的设计思想,多个ItemDecoration 各司其职,如本文,采用官方 ItemDecoration 作分割线,自己又写一个 ItemDecoration 作 分类title 和 分类title 相关的 悬停title。用时根据需要,选择任意数量的“装饰品” ItemDecoration,来丰富你的 RecyclerView。可能我的low常规思想还是一个XXX类,使用时如果扩充功能,需要extends and code~但这样不同的功能就太耦合了,不利于复用。毕竟 “组合大于继承”。

点击最后 阅读原文 查看源码。

更多

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号: