概述
最近翻开以前fork的github代码,发现了AmazingListView,是google官方当年的时候写的实现吸顶效果的ListView组件,然后最近在项目中有遇到需要吸顶效果的RecyclerView的需求,于是看了下源码,在AmazingListView的基础上,改进了一些bug,最终出现了今天的PinnedRecyclerView.
示例
最后的结果长这个样子.
吸顶标题高度小于普通节点.

吸顶标题高度大于普通节点.

谷歌AmazingListView当吸顶标题高度大于普通节点高度的时候会有跳变,效果如下,原因后面分析.

网上有介绍RecyclerView很好的文章,我个人比较喜欢这个,大家不妨先看看.
http://blog.csdn.net/lmj623565791/article/details/45059587
核心代码
两个核心类AmazingListView.java和AmazingAdapter.java.
AmazingAdapter继承自BaseAdapter并实现了SectionIndexer, OnScrollListener接口,在onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)的时候,不断去触发调整吸顶标题View的状态.
标题栏主要有三种状态,显示,不现实和上推过程中的部分显示,如代码中所定义.
在滚动的过程,始终去判断第一个可见元素的状态,在google的代码中,他们关心的是每个组别的最后一个节点的底部到父容器的顶部的距离的变化.
其中这三种状态的分类大致如下:
呐,从代码中可以看到他们上推的条件是从上一个组的最后一个切换到下一个组.
ok初步的状态判断到此就差不多完成了,接下来要关注AmazingListView了,这个类继承自ListView,反正我们把它看成是一个ViewGroup,我们需要将一个标题栏画出来,那么首先有这个几件事情要去做,
首先,new一个View出来,它的长相要和普通标题栏一摸一样,google直接用了同一个布局R.layout.item_composer_header,在普通节点里面只是将这个布局给隐藏了.
首先给ListView设置一个View作为吸顶头,然后在onMeasure获得其基本高度参与后面的计算,并且由于configureHeaderView中不断的调用了view的layout方法,那么会不断的触发onLayout方法,不断的根据参数放置view,最后通过
好了,现在到了最核心的地方了,就是根据传入的第一个可见位置,去计算吸顶view的状态,核心代码如下:
如果是不可见的,那么设置为不可见,后面就不绘制了,如果是可见的,放在最顶部,直接绘制,如果是处于上推状态的,那么要计算要显示的高度和一个上推程度,这样我们实现的时候可以根据这两个数据去控制前端交互.
好了,到此,原理就完全结束了,那么怎么用呢?其实主要就是去实现几个接口.来看几个主要的接口吧:
根据是否要显示标题来确定是否要隐藏,普通节点是要隐藏的,同时在此根据索引设置标题.
这里是设置标题的状态变化区域,alpha可以理解为上推程度.做了设置标题和透明度设置的两种操作,这里你也可以根据alpha去便宜文字也行.
这三个接口是SectionIndexer的标准接口,工作的时候通过他们来获取组相关的数据.
好了,到此怎么用也讲完了.
移植到RecyclerView
RecyclerView相较于ListView进行了解耦,把一个寻找索引的操作放在了LayoutManager中.根据以上的原理,那么移植起来其实也挺容易的,但是我觉得原来的接口写起来不是很直观的方便,那么就稍微DIY了一下,反正直接上代码吧,两个类第一版最后长这样.
PinnedRecyclerView和PinnedRecyclerAdapter
|
|
再来看看我们的Adapter类.
看看怎么用吧:
相较而言,主要是简化了设置view的操作,只需要提供两个布局就行了,我在代码里面自动设置了
绑定数据的时候也更方便,我把头和内容节点都给出来了.
反正,我觉得这样用着比原来方便了许多.好了,终于可以使用它来愉快的玩耍了.
bug发现了
咦,不对啊,从这个原理上去看,要是上一个单元的最后一个节点的高度还没有吸顶标题高度高,岂不是会跳变?改一下代码实现一下,将item_composer_header.xml的高度android:layout_height=”wrap_content”改为android:layout_height=”60dp”,那么结果就像上面的gif那样.
这不是我们想要的,那么如果用原来的思路,不然没法实现,于是,换机制呗,搞发搞发,有了,那么就算第一个完全可见标题节点离顶部的距离呗,好了,按照这个思路写下去,最后就有了我们最终版本的PinnedRecyclerView.
我们的Adapter.
可以看到,我在原来的基础上又优化了一些接口,实现起来更加直观,自己认为哈…
最后我们要实现的页面需要如下操作就行了:
总结
最终的代码放在了github上,链接是 https://github.com/gordon-rawe/pinned-recycler-view .有兴趣的来看一发.对了,我们的RecyclerView是支持多种节点类型的哦.
代码一共三个分支,bottom是一开始的根据上个组最后节点状态计算的机制实现的分支.top是最新的根据第一个可见标题节点距顶部距离计算机制实现的分支.
写博客好费劲啊,是时候来我的github上搓一下了star了撒.