我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:盛源彩票 > 泛型类 >

一步步教你实现完整的复杂列表布局

归档日期:04-25       文本归类:泛型类      文章编辑:爱尚语录

  8月8日傍晚,20余名乐视移动供应商,经过乐视总部所在地乐视大厦大堂的门禁,首次全体进入乐视大厦的办公区域,要求面见乐视移动代理CEO。谈及此次进入乐视大厦的目的,一名讨债的乐视移动供应商对澎湃新闻说,“还钱啊,(外界)不是传乐视要倒么,我们害怕啊。”据悉,乐视方面还欠这批在乐视大厦大堂讨债的21家乐视移动供应商3450余万元。

  本篇来自管思妥耶夫斯基的投稿,介绍了如何使用RecyclerView 实现复杂的布局,希望对大家有所帮助。

  我们在工作中遇到最多的视图场景恐怕就是各种样式的列表了,这也是由手机屏幕有限的尺寸决定的,随着需求的日益丰满,我们会发现列表的样式也随之做着各种各样的变更:样式越来越多了,布局越来越复杂了,如果我们前面的布局是单纯将各种ViewGroup拼接到一块的,那改动起来就费事了,暂且不说数据量大引起的卡顿问题,面临的工作量绝不是修改布局文件就能搞定的,数据的绑定、事件触发的设置、滑动的处理、手势冲突的解决…甚至还可能要加上些高级UI特效。打开淘宝或京东,我们能看到这样的布局样式:

  相信做过电商类app的朋友在产品拿到我们面前这样的页面时都纠结过如何去实现它,大概有这样几种思路:

  3、利用 RecyclerView 的多级嵌套实现,例如实现这样的布局:

  当数据量较大、分屏页数较多的时候,2 和 3 会出现明显的卡顿,这是因为 cpu 需要同时处理各个滑动布局的内部item位置关系以及数据的赋值,这样一来就容易出现 cpu 和 gpu 的计算与展示出现不同步的现象,导致屏幕显示丢帧,造成视觉卡顿的现象,尤其是当item的布局又很复杂时更容易出现此情况,甚至可能出现oom。

  4、使用 RecyclerView 实现全布局,用一个 RecyclerView 实现复杂的布局列表,这一种是完全符合谷歌的设计标准的,同时充分利用了 RecyclerView 双缓存的原理,下面我通过一个很典型的例子,带着大家一步步实现它,并通过该例子,解析以上几种常见的布局样式,相信看完后你可能跟我有同样的感觉:绝大部分的复杂列表都是有规可循的。

  RecyclerView 内部维护了一个二级缓存(算上用户设置的,实际上拥有三级缓存),这些缓存是由 RecyclerView 的一个final类型的内部类所管理的,实际上由其以下缓存变量决定:

  接着对数据分组,定义四个数组,分别记录每项分组的头部数据的 section 的位置,分组内的每一项的 position 的位置:

  其中的年级个班级可分别表示为 section 和 position;接着准备游标,标记各组的 view:

  源码里我尽可能的都加上了注释,这里我只截取关键部分,实际上最关键的部分是做各个 item 所在位置关系的计算:

  第1步:计算出 item 的总数量,这里定义了一个抽象方法,用来标识当前的分组是否含有 SectionFooter,有的线步:

  得到 item 的总数量后,初始化几个数组:初始化与 position 相对应的 section 数组,初始化与 section 相对应的 position 的数组,初始化当前位置是否是一个 Header 的数组,初始化当前位置是否是一个 Footer 的数组;第3步:

  通过计算每个 item 的位置信息,将上一步初始化后的数组填充数据,最终这几个数组保存了每个位置的 item 的状态信息,即:是否是 header,是否是 footer,所在的 position 是多少,所在的 section 是多少:

  这样我们的基本的多布局的 adapter 基类就算完成了,里面我定义了分项 item 的点击事件和取数据的方法;

  使用很简单,我们定义好各项的布局:Header的布局、Footer的布局、SectionHeader的布局、SectionFooter的布局、上拉加载的布局,接着实现各自的ViewHolder。

  然后就可以定义我们具体的适配器,让它继承自写好的 SectionedRecyclerViewAdapter,在里面完成数据的绑定与展示,在这里,有个地方需要注意的是,需要动态设置 SectionBody 的每个 item 长和宽,并设置其左右边距,需要做一个计算,看图:

  1、当滚动状态为 SCROLL_STATE_IDLE 时,判断当前 item 的总数是否填充满了一屏,如果没满,也就没有上拉加载了;

  2、当可见 item 的最后一个可见的 item 的位置与 item 的总数一致时,进行下一步;

  表示正在请求,请求结束后置为false,防止多次请求;这里有一个细节,就是滑动边界的容差值,当 childView 边界完全显示在界面中时才会检测成功.这就导致了一个可能的情况是只差一点点滑动到边界时,也不会检测成功而出发上拉加载的回调,所以要求很高的灵敏度,故加上上下两个容差值,我们认为,当滑动到接近边界时,就认为需要进行上拉加载了,关键代码如下:

  outRect 为包裹在 itemView 外层 View 的坐标参数,如下图,例如:设置 outRect.set(0,0,0,0); 表示 itemView 的四周没有任何的分割空间存在,set 的四个参数分别表示 外围View 距离 itemView 左边、上方、右边、下方四个方向的间隔距离,尤其需要注意的是,在此处设置了 outRect 的四个方向的参数之后,会默认将这个间距设置到 itemView 的四个方向的 padding 上,由此我们可以知道,getItemoffsets 这个方法是我们必须要复写且实现的。

  外围View 将绘制在 itemView 之上,即遮盖 itemView,当我们绘制一些特殊需求时,此方法很是受用,例如:为每个 item 绘制一个角标,表示其状态,例如很多商品都有热卖或者优惠券的角标,利用画笔将 bitmap 绘制出即可:

  最后补充的是,各子View的点击事件,实际上,在 SectionedRecyclerViewAdapter 之中已经预定义了点击回调的接口,我们依然可以通过 EventBus 这种广播框架便捷的实现我们的效果,不赘述。

  以上便是实现一个典型的复杂布局的全部思路过程,源码在最后放出,基于此,下面挨个突破各种不规则的复杂布局。

  对于空布局,我们希望在使用的时候只需要一行代码 setEmtyView(view),就行了,adapter 无数据时自动调用空布局,看下实现思路:

  先看淘宝的首页,也就是上面图1,整个滚动视图,顶部是一个广告轮播,接着是一个分类的网格视图,再接着是类似公告的广告显示区域,下面是一条分割线,再往下又是一个网格视图,广告轮播我们可以当做整个列表的顶部 Header,广告公告的视图当做每一个分组的 footer,不需要显示的做隐藏。

  当然分类区域每行的列数和分割线下面的推荐商品区域的每行列数有差异的,这个在 SpanSizeLookup 这个继承类中去控制即可。我们又看到,不仅列数不一致,连样式都变了!实际上,有两种方案可以控制样式的变动,一种是将所有的样式都写好到一个布局里面,控制其显示与隐藏(可以使用 ViewStub 做隐藏与显示),考虑到性能问题,不大推荐这种方法。

  思路有了,实现起来并不复杂。实际上,甚至连分割线以上的部分,即:轮播、分类、广告公告都可以成为列表的Header,只不过这样的话,分类需要我们单独去完成布局的构建,单省去了分组布局多类型的步骤。图2、3 和 我们的demo一样,按思路实现即可。

  声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。

本文链接:http://buggystordera.com/fanxinglei/13.html