RecyclerView实现瀑布流以及细节问题

RecyclerView实现瀑布流以及细节问题

1)使用 StaggeredGridLayoutManager

1
2
3
4
5
6
7
8
9
10
val layoutManager = StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
layoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE// 禁止左右交换
recyclerView.layoutManager = layoutManager
// decoration
recyclerView.addItemDecoration(StaggeredDividerItemDecoration(16, true))
// adapter
mAdapter = PjListVPAdapter()
recyclerView.adapter = mAdapter
// animator
recyclerView.itemAnimator = DefaultItemAnimator()

2)如果需要Item之间的间隔,就需要自定义 ItemDecoration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 瀑布流ItemDecoration
* 必须配合RecyclerView的StaggeredGridLayoutManager一起使用
*
* @author Dench
* @data 2020-09-04
*/
class StaggeredDividerItemDecoration(
space: Int, // 间隔 pix
private val includeEdge: Boolean = false // 是否显示边距
) : ItemDecoration() {
private val space = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
space.toFloat(),
Resources.getSystem().displayMetrics
).toInt()

override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val lp = view.layoutParams as StaggeredGridLayoutManager.LayoutParams
val lm = parent.layoutManager as StaggeredGridLayoutManager
val spanIndex: Int = lp.spanIndex
val spanCount: Int = lm.spanCount
if (includeEdge) {
outRect.left = space * (spanCount - spanIndex) / spanCount
outRect.right = space * (spanIndex + 1) / spanCount
outRect.top = space
} else {
outRect.left = space * spanIndex / spanCount
outRect.right = space * (spanCount - spanIndex - 1) / spanCount
outRect.bottom = space
}
}
}

3)滑动时闪烁,Item自动切换位置

解决方案 layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)

4)设置了 GAP_HANDLING_NONE 属性之后,顶端空白

由于ViewHolder的回收机制,滑回到上方时Item需要重新自行绘制,因为item的尺寸并不确定,导致重新绘制之后跟原来的高度不一致,又禁止了左右切换的调整策略,才导致的顶端空白。通常是因为加载图片之后重新计算item高度导致,尽可能在load图片之前先确定ImageView的高度。
这个时候只能给后台同学提需求了,在服务的Json数据需要包含图片的长宽信息。
附根据屏幕计算ImageView显示的宽高

1
2
3
4
5
6
7
private val width = (DensityUtil.getScreenWidth() - TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
68f,// 间距和padding
Resources.getSystem().displayMetrics
).toInt()) / 2 // spanCount
private val height = (width * 1.5).toInt()
imageView.layoutParams.height = height

5)刷新,删除,插入同样会导致顶端空白

使用 notifyDataSetChanged()方法做刷新时,会触发StaggeredGridLayoutManager 的onItemsChanged 方法,导致item的spanIndex重现进行计算,item所在列的位置出现了变化,导致了顶部空白。

使用notifyItemRangeInsertednotifyItemRangeChanged做局部刷新 则不会引起变化。

6)注意item等宽问题

采用StaggeredDividerItemDecoration的等宽计算方法。

作者

Dench

发布于

2020-09-04

更新于

2020-09-04

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×