RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法

前言

RecyclerView已经出来很久,现在几乎应该都会用RecyclerView代替Listview,虽然我觉得大多数人应该还是不太清楚这两者之前的区别的,或者说RecyclerView相对于Listview到底好在哪里。我平时也只是很简单的使用一下,并没有对其原理进行深度挖掘,现在刚好公司项目不忙,就花点时间研究一下它的源码。

内容

类继承关系

我觉得研究任何一个类的源码首先应该知道其类的继承关系,这样我们可以对它有一个整体的认识,比如TextView继承自View,那它就会有View的一些特性。所以先来看下RecyclerView的继承关系:
这里写图片描述

可以看到它直接继承于ViewGroup,所以它是个容器(废话,哈哈),还有它的子类有BaseGridView,WearableRecyclerView,HorizontalGridView,VerticalGridView,我们暂时这里不研究子类。

类注释

除了类继承关系,类注释也是很重要的部分,因为它往往介绍了这个类的特性,以及一些关键概念,我们这里就来看下RecyclerView的注释,原文如下

A flexible view for providing a limited window into a large data set.
Glossary of terms:
Adapter: A subclass of RecyclerView.Adapter responsible for providing views that represent items in a data set.
Position: The position of a data item within an Adapter.
Index: The index of an attached child view as used in a call to getChildAt(int). Contrast with Position.
Binding: The process of preparing a child view to display data corresponding to a position within the adapter.
Recycle (view): A view previously used to display data for a specific adapter position may be placed in a cache for later reuse to display the same type of data again later. This can drastically improve performance by skipping initial layout inflation or construction.
Scrap (view): A child view that has entered into a temporarily detached state during layout. Scrap views may be reused without becoming fully detached from the parent RecyclerView, either unmodified if no rebinding is required or modified by the adapter if the view was considered dirty.
Dirty (view): A child view that must be rebound by the adapter before being displayed.
Positions in RecyclerView:
RecyclerView introduces an additional level of abstraction between the RecyclerView.Adapter and RecyclerView.LayoutManager to be able to detect data set changes in batches during a layout calculation. This saves LayoutManager from tracking adapter changes to calculate animations. It also helps with performance because all view bindings happen at the same time and unnecessary bindings are avoided.

For this reason, there are two types of position related methods in RecyclerView:

layout position: Position of an item in the latest layout calculation. This is the position from the LayoutManager’s perspective.
adapter position: Position of an item in the adapter. This is the position from the Adapter’s perspective.
These two positions are the same except the time between dispatching adapter.notify* events and calculating the updated layout.

Methods that return or receive LayoutPosition use position as of the latest layout calculation (e.g. getLayoutPosition(), findViewHolderForLayoutPosition(int)). These positions include all changes until the last layout calculation. You can rely on these positions to be consistent with what user is currently seeing on the screen. For example, if you have a list of items on the screen and user asks for the 5th element, you should use these methods as they’ll match what user is seeing.

The other set of position related methods are in the form of AdapterPosition. (e.g. getAdapterPosition(), findViewHolderForAdapterPosition(int)) You should use these methods when you need to work with up-to-date adapter positions even if they may not have been reflected to layout yet. For example, if you want to access the item in the adapter on a ViewHolder click, you should use getAdapterPosition(). Beware that these methods may not be able to calculate adapter positions if notifyDataSetChanged() has been called and new layout has not yet been calculated. For this reasons, you should carefully handle NO_POSITION or null results from these methods.

When writing a RecyclerView.LayoutManager you almost always want to use layout positions whereas when writing an RecyclerView.Adapter, you probably want to use adapter positions.

呵呵,辣么大一串,就算是中文都懒的看,何况是英文,可四既然是写博客,就是要做出贡献,只能咬紧牙关看下去。经过吭哧吭哧的阅读翻译,大概意思如下:

RecyclerView是一个灵活的view,用来在有限的窗口中显示大量的数据集。呵呵,官方文档这么写,我也很绝望。
在真正开始阅读源码前,先介绍几个关键名词:

Adapter:RecyclerView.Adapter的子类,用来提供显示数据条目的视图(从介绍来看和ListView的adapter差不多)

Position:adapter中数据条目的位置

Index:已经添加的子view的索引,也就是item视图,在getChildAt(int)会被用到。和position要区别开来,position是数据的位置,index是视图的位置

Binding: 将adapter中的数据显示到每一个child view中的过程。

Recycle (view):就是一个可以复用的view,这可以大幅提高性能,因为省去了初始化和构造的过程

Scrap (view):什么是Scrap view呢?就是指的那些还没有被detached,但是已经被标记为removal或者reuse,Scrap views被复用的时候有两种情况,一种是完全不改变内容,因为数据没有发生改变,另外一种是当这个view被认是dirty,的时候,这样就要重新绑定数据(从后面代码来看,其实scrap的view指的是那些已经调用了detach方法,但并没有被remove的view,只是将parent设为null,在视图中仍然存在)

Dirty (view): dirty view指的是那些在展示之前必须重新绑定数据的view

上面提到的view都是指RecyclerView中的item view

关键名词介绍到这里,大家应该都看的懂。

接下来重点介绍了RecyclerView中positions概念

大概意思是Recyclerview在RecyclerView.Adapter和RecyclerView.LayoutManager之间采用了一种额外的抽象,使得可以在布局计算的时候侦测到大批量数据的变化,这可以将LayoutManager从跟踪adapter的数据变化中解脱出来,而去从事计算动画的工作。( to calculate animations这段意思不确定,特别是to在这里的意思,先放着,等看了源码再说)。这样还可以提高性能表现,因为所有的view binding在同一时间完成,避免了不要的binding(一脸闷逼,还是先放着)。

因为这个原因,所以在RecyclerView中有两类position相关的方法。

layout position: 在最近一次布局计算后item的位置,这个位置是站在LayoutManager的角度来说
adapter position: item在adpater中的位置,这是站在Adapter的角度来说

个人认为layout position就是指视图上item的位置,而adapter position就是指在data set中的位置
这两个position在大多数时候是相等的,只有当adapter.notify*已经被调用,而布局还没有刷新之前这段时间是不一样的。

那些返回或者接受*LayoutPosition*的方法使用的是最近一次布局计算的位置,比如getLayoutPosition()findViewHolderForLayoutPosition(int)。这些position就是用户在屏幕上看到的样子。举个例子,如果你有一个list在屏幕上展示,然后用户想要第五个条目,你就应该使用这些方法。

另外一系列position相关的方法格式是这样的*AdapterPosition*,如getAdapterPosition(), findViewHolderForAdapterPosition(int)。如果你需要和最新的adapter position打交道,不管它是否已经反映到布局中,那你就应该使用这系列方法。比如,如果你希望在ViewHolder click中访问adapter中的item,你就应该使用getAdapterPosition(),有一点要注意,就是当notifyDataSetChanged已经被调用,而布局还没有计算完成,这时候就不能使用这些方法去计算adapter的position。所以,当你在使用这些方法的时候需要特别注意处理返回值为NO_POSITION 的情况。

总的来说就是当你在写RecyclerView.LayoutManager的时候,基本上就应该使用layout positions,而在写Adapter的时候就应该使用adapter positions

你看,仔细看一下注释还是很有用的吧。

构造函数

好了接下来就是真正开始啃源码的时候了。首先当然从构造函数开始,既然是继承自View,那肯定也就是少不了View类似的那些构造函数。
这里写图片描述

先从RecyclerView(Context context)看起。

 public RecyclerView(Context context) {
        this(context, null);
    }

直接调用RecyclerView(Context context, AttributeSet attrs),

public RecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

所以最后就看public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)
源码不是很多,就全部贴上来了

 public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
            mClipToPadding = a.getBoolean(0, true);
            a.recycle();
        } else {
            mClipToPadding = true;
        }
        setScrollContainer(true);
        setFocusableInTouchMode(true);

        final ViewConfiguration vc = ViewConfiguration.get(context);
        mTouchSlop = vc.getScaledTouchSlop();
        mScaledHorizontalScrollFactor =
                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
        mScaledVerticalScrollFactor 
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值