RecyclerView 源码学习(二):一步一步自定义LayoutManager

本文详细介绍了如何从头开始自定义一个RecyclerView的LayoutManager,包括理解Recycler机制,自定义布局参数,处理滑动事件及添加动画效果,提供了一个简易实现的案例。
摘要由CSDN通过智能技术生成

前言

这篇文章实现了一个简单的LayoutManager,重点在于从原理方面一步一步去了解如何自定义一个LayoutManager。麻雀虽小,但五脏俱全。从自定义LayoutManager的layout布局,到scroll滑动,再到添加简单的动画效果。 其实,自定义一个LayoutManager也没那么难。

基本概念

Recycler

LayoutManafger调用 getViewForPosition 获取一个item,Recycler会决定是从缓存返回还是生成新的item。在自定义LayoutManger的时候,要保证不可见的视图被传递给Recycler。

Scrap 或 Recycler

Recycler是二级缓存,一个scrap heap 和一个 recyle pool, scrap 中的数据是正确的数据,比如我们快速上下滑动列表时,在边缘的栏目一会显示一会消失,所以会放在scrap中。 而已经消失并不使用的item,会被放在recyle中,其中的数据也是不正确的。

每次LayoutManager去请求一个视图调用getViewForPosition的时候,会先从scrap heap中找,存在直接返回。否则去recyle pool 中找一个视图,然后重新在adapter中绑定数据。最终如果还没有缓存,调用我们在adapter中重写的onCreateViewHolder,生成一个新的ViewHolder绑定数据并返回。

使用 detachAndScrapView 将视图放进scrap中去,使用removeAndRecycleView 将可能不会再用的视图放回recycler并且后续如果使用,还要进行rebind

小结

其实,上面这些都是废话,只要知道要获得一个view和用完一个view,都要通过recycler。常用的方法有getViewForPosition ,detachAndScrapView 和 removeAndRecycleView

自定义LayoutManager

generateDefaultLayoutParams

作用:控制每个item的layoutParams
为每一个childView设置的LayoutParams在这个方法中返回。很简单,一般我们都直接返回一个WrapContent的lp

初始布局 onLayoutChildren

这个方法会在一个view 第一次执行layout的时候调用,同时也会在adaper的数据集改变并通知观察者(也就是view)的时候调用。所以在其中每一次布局的时候,要先将之前放置的无用的View放回recycler中,因为这些View我们在后续还可能使用,为了减少初始化以及bind的时间,我们调用detachAndScrapAttachedViews。此外,对于不会再用到的View,可以调用removeAndRecycleView进行回收。

        if (getItemCount() == 0) {
            offset = 0;
            detachAndScrapAttachedViews(recycler);
            return;
        }

这里自定义的LayoutManager比较简单,假定全部的item都是相同的大小。所以可以在一开始进行测绘:

        if (getChildCount() == 0) {
            View scrap = recycler.getViewForPosition(0);
            addView(scrap);
            measureChildWithMargins(scrap, 0, 0);
            mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
            mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
            startLeft = (getHorizontalSpace() - mDecoratedChildWidth) / 2;
            startTop = (getVerticalSpace() - mDecoratedChildHeight) / 2;
            interval = 10;
            detachAndScrapView(scrap, recycler);
        }

这里注意getItemCount和getChildCount的区别:前者是adapter中添加的数据的数目,而后者是当前recyclerView中已经添加的子View的数目。所以上述代码的含义就是,如果没有添加过子View,那么从recycler中取出一个并完成测绘:

recycler.getViewForPosition(0);
            addView(scrap);

测绘完成后,再重新放回recycler中,调用

detachAndScrapView(scrap, recycler);

最后,再将之前添加的全部子View放回recycler中,因为一会还要使用,为了避免rebind,调用

        detachAndScrapAttachedViews(recycler);

然后就可以进行layoutChildren的过程了。

先来一个简单的,如下:

int left = 100, top = 0;
        for (int i = 0; i< getItemCount(); i++) {
            if (outOfRange(top)) continue;
            View scrap = recycler.getViewForPosition(i);
            measureChildWithMargins(scrap, 0, 0);
            addView(scrap);
            layoutDecorated(scrap, left, top, left + mDecoratedChildWidth, top + mDecoratedChildHeight);
            top += mDecoratedChildHeight + interval;
        }

基本效果就是这样:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值