自定义RecyclerView悬浮吸顶效果的LayoutManager

本文详细介绍了如何自定义RecyclerView的LayoutManager以实现悬浮吸顶效果,通过分析LinearLayoutManager的源码,理解其缓存复用机制,并在此基础上实现自定义布局。在过程中,讨论了缓存池的管理,优化滚动时的ViewHolder创建,最终达到平滑滚动和高效复用的效果。文章还指出,自定义布局虽然实现了悬浮吸顶,但不支持定向刷新和动态增删,适用于数据变化少且需要特定Item悬停的场景。
摘要由CSDN通过智能技术生成

时间过得飞快,距离上次写博客又过了一个半月,想了想不该偷懒下去了,然后就把前段时间写的一个 LayoutManager 拿出来写一下。RecycleView 相信大家一定十分熟悉了,相比 ListView 它具备更多级的缓存以及定向刷新的特性,除此之外也就是本文要说的自定义 LayoutManager 也是十分的靓仔,和传统使用 ViewGroup 布局相比,它增加了回收和复用子 Item 的能力,用起来是真香。

一般在做通讯录的时候,我们会为用户分组,从而绘制的时候也需要绘制各组的头部,如果在使用 RecycleView 来实现这种效果的时候,通常我们会使用 ItemDecoration ,ItemDecoration 在测量时为每个(或指定的)子 View 的 LayoutParams 设置四边的偏移量,然后 LayoutManager 在使用 layoutDecoratedWithMargins() 时会根据相应的偏移量来布局 childView ,我们再根据 childView 之间的留白在 ItemDecoration#onDraw() 时来绘制我们的分组头部,在 ItemDecoration#onDrawOver() 时绘制被移出屏幕的最近一个头部,最终达到我们要的效果。不过,如果我们想要这个头部支持点击怎么办?ItemDecoration 顾名思义就是为 Item 添加装饰物,是不支持点击交互的,那么就需要我们另外想法子了。我的方法是自定义一个 LayoutManager。

先见效果图:

先声明以下 LLM 为 LinearLayoutManager 的简写),FLM 为本文自定义LayoutManger的简称。

自定义 LayoutManager 第一步,返回自定义的 LayoutParams 

@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}

 这里我们要做的布局类似于线性布局,所以采用宽高都有view自己决定的测量参数,如果是网格布局或者是交错式网格布局,则会根据方向进行调整。

然后就是第二步,重写 onLayoutChildren 方法,我们先来看一看 LLM 在这里是怎么写的,代码很长,所以简略掉其中各种判断,我们只贴其中的关键行:

    /**
     * {@inheritDoc}
     */
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        ......

        // 不需要布局,移除并回收所有的view
        if (mPendingSavedState != null || mPendingScrollPosition != RecyclerView.NO_POSITION) {
            if (state.getItemCount() == 0) {
                removeAndRecycleAllViews(recycler);
                return;
            }
        }
        ......

        // 收集计算锚点信息
        ......

        // 预布局动画相关代码
        ......

        // 遍历所有的 child 根据其 ViewHolder 的不同状态将 ViewVolder 添加到不同的缓存集合
        detachAndScrapAttachedViews(recycler);
        ......
        // 根据布局方向填充
        if (mAnchorInfo.mLayoutFromEnd) {
            // fill towards start
            ......
            fill(recycler, mLayoutState, state, false);
            ......
        } else {
            // 另外一个方向
            ......
        }
        // 修正偏差,LinearLayoutManager内部的scrap缓存池未移除的view进行布局(应该是与动画相关)
        ......
    }

然后是 fill() 方法里面的关键行:

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
        ......
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            
            ......
            // 这里进行单个view进行布局
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            ......
        }
        ......
        return start - layoutState.mAvailable;
    }


void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutState layoutState, LayoutChunkResult result) {
        View view = layoutState.next(recycler);
        ......

        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        if (layoutState.mScrapList == null) {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addView(view);
            } else {
                addView(view, 0);
            }
        } else {
            if (mShouldReverseLayout == (layoutState
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值