7、LayoutManager 详解:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager
RecyclerView 之所以强大,很大程度上要归功于 LayoutManager。你可以把它想象成一个「布局指挥官」——它决定了每个 item 怎么摆、怎么滚动、怎么复用。我刚开始接触 RecyclerView 时,以为它和 ListView 差不多,直到我深入研究了 LayoutManager 的源码……嗯,那完全是另一个世界。
说白了,LayoutManager 就是 RecyclerView 的灵魂。没有它,RecyclerView 只是一块空白的画布。今天我就带你逐个剖析三种最常用的 LayoutManager,把我这些年踩过的坑、积累的经验一并分享给你。
7.1 LinearLayoutManager:最常用的线性布局
LinearLayoutManager 是最基础、最常用的 LayoutManager。它把 item 排成一列(垂直)或一行(水平),和传统的 ListView 类似,但灵活得多。
我个人习惯用 LinearLayoutManager 来处理绝大多数列表场景。为什么?因为它简单、高效、可控。你想想看,一个聊天列表、一个通知列表、一个设置页面——这些用 LinearLayoutManager 就够了。
// 最基本的用法
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
// 水平滚动
val horizontalManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerView.layoutManager = horizontalManager
// 从底部开始堆叠(比如聊天界面)
val stackFromEndManager = LinearLayoutManager(this)
stackFromEndManager.stackFromEnd = true
recyclerView.layoutManager = stackFromEndManager
stackFromEnd = true 并配合 reverseLayout = false。这样新消息会出现在底部,而且滚动到底部时不会闪烁。我曾经在这上面吃过亏——没设置 stackFromEnd,结果每次插入新消息,列表都从顶部开始跳,用户体验极差。
LinearLayoutManager 还有一个很实用的方法:findFirstVisibleItemPosition() 和 findLastVisibleItemPosition()。我在做阅读类 App 时,经常用它们来记录用户的阅读位置。
// 获取当前可见的第一个和最后一个 item 位置
val firstPos = layoutManager.findFirstVisibleItemPosition()
val lastPos = layoutManager.findLastVisibleItemPosition()
// 滚动到指定位置
layoutManager.scrollToPosition(10)
// 平滑滚动
layoutManager.smoothScrollToPosition(recyclerView, null, 20)
7.2 GridLayoutManager:网格布局的利器
GridLayoutManager 把列表分成网格,每个 item 占据一个或多个单元格。它继承自 LinearLayoutManager,所以 LinearLayoutManager 的特性它基本都有,只是多了一个「列数」的概念。
说白了,GridLayoutManager 就是 LinearLayoutManager 的「网格版」。你设置一个 spanCount(列数),它就把屏幕水平方向分成这么多列,然后 item 从左到右、从上到下排列。
// 3 列网格
val gridManager = GridLayoutManager(this, 3)
recyclerView.layoutManager = gridManager
// 水平滚动的网格
val horizontalGrid = GridLayoutManager(this, 3, GridLayoutManager.HORIZONTAL, false)
recyclerView.layoutManager = horizontalGrid
GridLayoutManager 最强大的功能是 SpanSizeLookup。它允许某个 item 跨越多列。比如在相册中,第一张图可以占两列,其他图占一列。
val gridManager = GridLayoutManager(this, 4)
gridManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position == 0) {
4 // 第一个 item 占满整行
} else {
1 // 其他 item 占一列
}
}
}
recyclerView.layoutManager = gridManager
7.3 StaggeredGridLayoutManager:瀑布流布局
StaggeredGridLayoutManager 实现的是「瀑布流」效果。每个 item 的高度可以不同,它们像瀑布一样错落有致地排列。这在图片展示类 App 中非常常见——比如 Pinterest、花瓣网。
我记得第一次实现瀑布流时,直接用了 GridLayoutManager,结果图片被强行拉伸成一样高,丑得不行。后来换成 StaggeredGridLayoutManager,效果立马就对了。
// 2 列瀑布流,垂直方向
val staggeredManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
recyclerView.layoutManager = staggeredManager
// 3 列水平瀑布流
val horizontalStaggered = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.HORIZONTAL)
recyclerView.layoutManager = horizontalStaggered
使用 StaggeredGridLayoutManager 时,有几个坑需要特别注意:
- item 高度必须不同——如果所有 item 高度一样,那它和 GridLayoutManager 没区别
- 位置获取方式不同——不能用 findFirstVisibleItemPosition,要用 findFirstVisibleItemPositions(返回数组)
- item 动画可能异常——删除或添加 item 时,瀑布流的动画效果有时会「跳一下」
// 获取可见位置(返回数组,因为每列的第一个可能不同)
val firstVisibleItems = IntArray(staggeredManager.spanCount)
staggeredManager.findFirstVisibleItemPositions(firstVisibleItems)
val lastVisibleItems = IntArray(staggeredManager.spanCount)
staggeredManager.findLastVisibleItemPositions(lastVisibleItems)
7.4 三种 LayoutManager 的对比
| 特性 | LinearLayoutManager | GridLayoutManager | StaggeredGridLayoutManager |
|---|---|---|---|
| 布局方向 | 垂直/水平 | 垂直/水平 | 垂直/水平 |
| 列数/行数 | 1 列 | 固定列数 | 固定列数 |
| item 高度 | 可不同 | 可不同(但网格对齐) | 可不同(瀑布流) |
| 跨列支持 | 不适用 | 支持(SpanSizeLookup) | 不支持 |
| 性能 | 最高 | 较高 | 一般(布局计算复杂) |
| 适用场景 | 列表、聊天、设置 | 相册、商品列表 | 图片瀑布流、卡片墙 |
7.5 如何选择?我的建议
选 LayoutManager 其实没那么复杂。我一般按这个思路来:
- 90% 的场景用 LinearLayoutManager——别想太多,默认用它就对了
- 需要网格展示时用 GridLayoutManager——比如商品列表、图标网格
- 只有图片类、且高度差异大时用 StaggeredGridLayoutManager——别滥用,它性能开销大
你想想看,很多 App 其实根本用不到瀑布流。强行用 StaggeredGridLayoutManager 反而会引入不必要的复杂度。我见过一个新闻列表 App,明明每条新闻高度差不多,却用了瀑布流,结果滑动时经常卡顿——这就是典型的「杀鸡用牛刀」。
好了,关于 LayoutManager 的三种实现,我就讲到这里。记住:LinearLayoutManager 是基础,GridLayoutManager 是网格,StaggeredGridLayoutManager 是瀑布流。理解它们的特性和适用场景,你就能在项目中游刃有余了。