专栏名称: 郭霖
Android技术分享平台,每天都有优质技术文章推送。你还可以向公众号投稿,将自己总结的技术心得分享给大家。
目录
相关文章推荐
51好读  ›  专栏  ›  郭霖

掌握自定义LayoutManager之实现流式布局

郭霖  · 公众号  · android  · 2016-12-07 08:00

正文

今日科技快讯

昨日,人民币兑美元汇率闹出巨大乌龙事件,短短两个小时,Google上查询的人民币兑美元的汇率从6.8 : 1变成了7.4 : 1 ,人民币资产瞬间缩水了约8%!一时间引起了极多人的恐慌。

后经查实,Google使用的是一家xe rate的网站数据,除了这家网站以外,其他网站的汇率数据都还保持在1 : 6.8左右,算是Google闹出了一个大的乌龙事件,也让很多人松了一口气。

作者简介

本篇是 张旭童 的第四篇投稿了,这是一个系列文章,我选取了实战部分分享给大家,如果对LayoutManager还不熟悉的朋友,可以去作者博客看看前置文章。

张旭童 的博客地址:

http://blog.csdn.net/zxt0601

概述

在开始之前,我想说,如果需求是每个Item宽高一样,实现起来复杂度比每个Item宽高不一样的,要小10+倍。

然而我们今天要实现的流式布局,恰巧就是至少每个Item的宽度不一样,所以在计算坐标的时候算的我死去活来。先看一下效果图:

艾玛,换成妹子图后貌似好看了许多,我都不认识它了,好吧,项目里它一般长下面这样:

往常这种效果,我们一般使用自定义ViewGroup实现,我以前也写了一个:

自定义VG实现流式布局

http://blog.csdn.net/zxt0601/article/details/50533658

这不最近再研究自定义LayoutManager么,想来想去也没有好的创意,就先拿它开第一刀吧。

(后话:流式布局Item宽度不一,不知不觉给自己挖了个大坑,造成拓展一些功能难度倍增,观之网上的DEMO,99%Item的大小都是一样的,so,这个系列的下一篇我计划 实现一个Item大小一样 的酷炫LayoutManager。但是最终做成啥样的效果还没想好,有朋友看到酷炫的效果可以告诉我,我去高仿一个。)

自定义LayoutManager的步骤:

以本文的流式布局为例,需求是一个垂直滚动的布局,子View以流式排列。先总结一下步骤:

  • 实现 generateDefaultLayoutParams()

  • 实现 onLayoutChildren()

  • 竖直滚动需要 重写canScrollVertically()和scrollVerticallyBy()

下面我们就一步一步来吧。

实现generateDefaultLayoutParams

如果没有特殊需求,大部分情况下,我们只需要如下重写该方法即可。


RecyclerView.LayoutParams 是继承自 android.view.ViewGroup.MarginLayoutParams 的,所以可以方便的使用各种margin。

这个方法最终会在 recycler.getViewForPosition(i) 时调用到,在该方法浩长源码的最下方:


重写完这个方法就能编译通过了,只不过然并卵,界面上是一片空白,下面我们就走进onLayoutChildren()方法 ,为界面添加Item。

注: 99%用不到的情况:如果需要存储一些额外的东西在LayoutParams里,这里返回你自定义的LayoutParams即可。

当然,你自定义的LayoutParams需要继承自 RecyclerView.LayoutParams。

onLayoutChildren

该方法是LayoutManager的入口。它会在如下情况下被调用:

  • 在RecyclerView初始化时,会 被调用两次

  • 在调用adapter.notifyDataSetChanged()时,会被调用。

  • 在调用setAdapter替换Adapter时,会被调用。

  • 在RecyclerView执行动画时,它也会被调用。即RecyclerView 初始化 、 数据源改变时 都会被调用。

(关于初始化时为什么会被调用两次,我在系列第一篇文章里已经分析过。)

在系列开篇我已经提到,它相当于 ViewGroup 的 onLayout() 方法,所以我们需要在里面layout当前屏幕可见的所有子View, 千万不要layout出所有的子View 。本文如下编写:


这个 fill(recycler, state) 方法将是你自定义LayoutManager之旅一生的敌人,简单的说它承担了以下任务:

在考虑滑动位移的情况下:

  • 回收所有屏幕不可见的子View

  • layout所有可见的子View

在这一节,我们先看一下它的简单版本,不考虑滑动位移,不考虑滑动方向等,只考虑初始化时, 从头至尾 ,layout所有可见的子View,在下一节我会配合滑动事件放出它的完整版.


用到的一些工具函数(在系列开篇已介绍过):


如上编写一个超级简单的fill()方法,运行,你的程序应该就能看到流式布局的效果出现了。

可是千万别开心,因为痛苦的计算远没到来。

如果这些都看不懂,那么我建议:

  • 直接下载完整代码,配合后面的章节看,看到后面也许前面的就好理解了= =。

  • 去学习一下自定义ViewGroup的知识。

此时虽然界面上已经展示了流式布局的效果,可是它并不能滑动,下一节我们让它动起来。

动起来

想让我们自定义的LayoutManager动起来,最简单的写法如下:


offsetChildrenVertical(-realOffset) 这句话移动所有的childView.

返回值会被RecyclerView用来判断是否达到边界, 如果返回值 != 传入的dy,则会有一个边缘的发光效果,表示到达了边界。而且返回值还会被RecyclerView用于计算fling效果。

写完编译,哇塞,真的跟随手指滑动了,只不过能动的总共就我们在上一节layout的那些Item,Item并没有回收,也没有新的Item出现。

好了,下面开始正经的写它吧:


这里用realOffset变量保存实际的位移,也是return 回去的值。大部分情况下它=dy。







请到「今天看啥」查看全文