一、吸顶效果的核心实现原理
RecyclerView 的吸顶效果,其核心思想是利用 ItemDecoration 的自定义绘制能力,在所有 Item 绘制完成后,动态地在 RecyclerView 顶部绘制一个悬浮的 Header。
1. 绘制流程的利用
RecyclerView的绘制流程为:onDraw-> 绘制ItemDecoration.onDraw()->dispatchDraw(绘制所有子 View) -> 绘制ItemDecoration.onDrawOver()。onDrawOver():由于它在所有子 View 绘制完成后才执行,因此其内容可以覆盖在所有 Item 之上,天然适合用于绘制悬浮 Header。
2. 核心三步
- 查找:在
onDrawOver()中,找到第一个可见的 Item,并判断它所属的组头是否应该吸顶。 - 测量与布局:获取 Header
View,并对其进行测量和布局,以确定其尺寸。 - 绘制:根据滚动状态,计算 Header 的绘制位置,并将其绘制到
Canvas上。
二、底层机制与源码分析
1. ItemDecoration的绘制机制
ItemDecoration的onDrawOver()方法提供了Canvas,允许开发者直接在RecyclerView的画布上进行绘制。- 在
onDrawOver()中,开发者需要手动调用headerView.draw(c)方法,将 HeaderView的内容绘制到Canvas上。
2. 布局与测量
LayoutManager提供了获取 Item 位置和尺寸的 API,如getDecoratedTop()。- 在
onDrawOver()中,我们需要手动调用View.measure()和View.layout()方法,对 HeaderView进行测量和布局。但为了优化性能,我们应该缓存 Header 的尺寸,避免在每次绘制时都进行重复的测量。
3. 滚动监听
- 通过
RecyclerView.OnScrollListener监听滚动事件,当滚动发生时,调用RecyclerView.invalidate()触发onDrawOver()的重绘。
三、通用解决方案与优化
1. 外部接口与数据模型
- 为了使
StickyHeaderDecoration具备通用性,我们可以定义一个接口(如StickyHeaderInterface),让Adapter来实现。这个接口提供了判断 Item 是否为 Header、获取 Header 数据等方法。
2. Header View的复用
- 为了优化性能,我们应该复用 Header
View,避免在每次绘制时都创建新对象。 - 方案:创建一个
ViewHolder缓存池,用于缓存 HeaderView。在onDrawOver()中,优先从缓存池中获取ViewHolder,然后调用onBindHeaderView()绑定数据。
3. 事件处理
- 点击穿透:由于 Header 是在
onDrawOver()中绘制的,它实际上并没有一个真实的View实例。因此,它的点击事件不会被系统自动处理。 - 解决方案:通过
RecyclerView.addOnItemTouchListener()监听触摸事件。在onInterceptTouchEvent()中,判断触摸坐标是否在 Header 的绘制区域内。如果是,onInterceptTouchEvent()返回true,并手动处理 Header 的点击事件。