item重排也要丝滑动画?三步开启SpannedGridLayoutManager的流畅过渡效果
2026/9/24 22:52:43 网站建设 项目流程

item重排也要丝滑动画?三步开启SpannedGridLayoutManager的流畅过渡效果

【免费下载链接】SpannedGridLayoutManagerAndroid RecyclerView.LayoutManager that resizes and reorders views based on SpanSize项目地址: https://gitcode.com/gh_mirrors/sp/SpannedGridLayoutManager

SpannedGridLayoutManager 是一款开源 Android 库,它继承自RecyclerView.LayoutManager,能够根据每个 item 的 SpanSize(行/列跨度)自动缩放并重新排布网格中的所有视图——比如把某个 item 放大成 2×2,其余 item 会自动流动填补缺口。但默认情况下,重排往往是一下子"硬切"过去的。这篇指南将带你用3 步让重排过程拥有丝滑的平移、缩放过渡动画。

为什么默认重排是"生硬"的?

RecyclerView 的 item 变化动画由 ItemAnimator 驱动,而它的核心工作方式是:通过稳定 ID 追踪同一个 item 在两次布局前后的位置,然后计算位移、执行过渡。

如果你的 Adapter 没有提供稳定 ID,每次重排后所有 item 在动画器眼里都是"新面孔",于是直接瞬移——这正是很多开发者觉得"重排不流畅"的根本原因。

所以,开启动画的关键不在于布局管理器本身,而在于补齐这三件事:

  1. 开启 Stable ID;
  2. 返回稳定的 item ID;
  3. 用正确的通知方式触发重新布局。

三步开启丝滑过渡动画

第一步:在 Adapter 中开启 Stable ID

只需一行代码,声明"本 Adapter 的 item 拥有稳定 ID":

setHasStableIds(true)

项目示例代码里就在初始化时完成了这一步,可参考sample/src/main/java/com/arasthel/spannedgridlayoutmanager/sample/GridItemAdapter.kt

第二步:重写 getItemId,返回真正稳定的 ID

override fun getItemId(position: Int): Long = position.toLong()

⚠️ 注意"稳定"的含义:同一条数据在任何时候都必须返回同一个 ID。请避免使用随机 hashCode,否则动画器会把同一个 item 误判为"旧 item 消失 + 新 item 出现",动画直接失效。示例中直接把 position 映射为 ID(position.toLong()),简单可靠。

第三步:用 notifyDataSetChanged() 触发整局重排

当 item 的 SpanSize 发生变化时(例如某张卡片从 1×1 放大为 2×2),请调用:

adapter.notifyDataSetChanged()

布局管理器会执行一轮完整重排,动画器对比出每个 ID 的新旧帧位置,平移与缩放过渡便自动开始。

完整示例:点击卡片放大,全网格流动

下面是一个典型用法(与示例 App 一致):4 列网格,点亮的 item 占 2×2,其余占 1×1,点击后触发重排动画。

val layout = SpannedGridLayoutManager(Orientation.VERTICAL, spans = 4) layout.spanSizeLookup = SpannedGridLayoutManager.SpanSizeLookup { pos -> if (adapter.clickedItems[pos]) SpanSize(2, 2) else SpanSize(1, 1) } recyclerView.layoutManager = layout recyclerView.adapter = adapter

完整可运行代码见sample/src/main/java/com/arasthel/spannedgridlayoutmanager/sample/MainActivity.ktsample/src/main/java/com/arasthel/spannedgridlayoutmanager/sample/GridItemAdapter.kt

动画背后的排布算法:空闲矩形拆分 📐

为什么 item 能"填补缺口"、甚至无视原有顺序重新排布?核心在 SpannedGridLayoutManager.kt 中的RectsHelper

  • 初始时,整个网格是1 个空闲矩形
  • 每放入一个 item,就把被占用的矩形沿左、右、上、下切分成最多 4 个子矩形(如右图所示);
  • 下一个 item 总是落入第一个能容纳它的空闲位置——这就是"先补近处空洞"的视觉效果来源,也是丝滑动画能逐帧追踪每个 item 位移的几何基础。

该算法的详细推导可以查阅项目根目录的free_space_algorithm.pdf

常见坑与小贴士 ✅

  • SpanSize 越界会抛异常:跨度必须落在1 ~ spans之间,否则会抛出InvalidSpanSizeException(定义于 InvalidSpanSizeException.kt)。
  • 滚动位置恢复默认关闭:因为重排可能大幅改变 item 顺序,为避免滚动位置错乱,LayoutManager 默认不恢复滚动位置。若你确信顺序变化不大,可设置itemOrderIsStable = true重新开启。
  • SpanSize 计算很复杂时:给spanSizeLookup打开缓存(usesCache = true),数据变化后调用invalidateCache()刷新,避免重复计算。

总结

让 SpannedGridLayoutManager 的重排动画"活"起来,本质上只有三步:开启 Stable ID → 返回稳定 ID → notifyDataSetChanged 触发重排。三步全部就位后,放大、缩小、换位都会获得连贯的过渡效果——这正是"自动填满空隙"类网格布局想要达到的理想体验。🎬

【免费下载链接】SpannedGridLayoutManagerAndroid RecyclerView.LayoutManager that resizes and reorders views based on SpanSize项目地址: https://gitcode.com/gh_mirrors/sp/SpannedGridLayoutManager

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询