踩坑之StaggeredGridLayoutManager

560 阅读2分钟

StaggeredGridLayoutManager 简单介绍

RecyclerView中布局管理器,主要用于瀑布流的实现。

StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)

主要有两个参数

  1. 行数/列数,取决于第二行参数
  2. 垂直还是水平方向

意外之喜

当我简单的给RecyclerView设置StaggeredGridLayoutManager 之后,发现下面的问题

事例视频

当滑动到最上面的时候,左右两列发生交换,这本质是发生了重新排序

初步解决方案

查询了一下网上资料 解决方案

 recyclerView?.layoutManager =
                StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL).apply {
                    gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE //防止Item互换
  }

gapStrategy有两种

  • GAP_HANDLING_NONE : 什么也不处理
  • GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS(默认值): 当滚动状态变化时,StaggeredGrid 会检查是否因为元素跨度过大导致出现空隙,如果发现空隙,它会重新布局,并使用动画元素移动到正确位置

使用上面的代码,禁止GAP处理又发现一个问题

滑动到顶部的时候,**同一行上下对不齐**的问题, 顶部留白问题。

我们捋一下这个问题,GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS 的重排序导致了item发生交换,禁用重排序有导致顶部空白的问题。

为什么重排序导致了item发生交换和空白?

xx.png 如图所示,向下滑动的时候,排列顺序如下,如果滑动到底部,在向上滑动,会发现,3的位置从原来的第1列跑到第二列,2的位置从原来的第2列跑到在第一列。

如果不进行重新排序,就会导2和1 顶部空白,如果重新排序,就会再次根据空隙,把2的位置放到第2列区,所以视觉上,你会看到左右交换了一下。

最终方案解决

  1. 禁用重排序
 recyclerView?.layoutManager =
                StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL).apply {
                   gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE //防止Item互换
  }

2. 到首页的时候要求重新布局

/**
     * 修复瀑布流顶部空白问题和间距错乱问题
     * 在onScrolled中调用
     */
    fun fixWaterfallFlowBlank(recyclerView: RecyclerView) {
        val layoutManager = recyclerView.layoutManager
        if (layoutManager !is StaggeredGridLayoutManager) {
            return
        }
        try {
            val firstVisibleItemPositions = IntArray(layoutManager.spanCount)
            layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)
            val firstVisibleItemPosition = firstVisibleItemPositions[0]
            if (firstVisibleItemPosition <= 1) { //滚动到顶部,瀑布流2列,position可能是0或者1
                layoutManager.invalidateSpanAssignments() //重新布局
                recyclerView.invalidateItemDecorations() //间距错乱问题
            }
        } catch (e: Exception) {
            Logger.e(e)
        }
    }