RecyclerViewTECH_ANDROIDrecycleView

RecyclerView 知识梳理(1) - 综述

2017-04-03  本文已影响945人  泽毛

一、概述

对于RecyclerView的学习,主要是需要掌握以下几点:

要理解整个RecyclerView的思想,有一个视频是一定要看的:RecyclerView ins and outs - Google I/O 2016。今天,我们就通过这个视频,把上面所学到的东西串联起来。

二、为什么要使用RecyclerView

RecyclerView诞生的目的就是为了替代ListView,我们先总结一下在使用ListView过程当中所遇到的问题:

if (convertView == null) {
     //通过LayoutInflator生成convertView,并产生一个ViewHolder,通过setTag关联起来.       
} else {
    //通过getTag获取ViewHolder,进行更新操作.
}

如果之前有了解过RecyclerView的基本用法,那么你会发现,对于上述这些问题,它都给出了自己的解决方案:

了解了这些,我们就能知道RecyclerView能帮我们解决什么问题,也就能更好地理解它为什么要这么设计,下面就开始进入真正的RecyclerView的学习。

三、RecyclerView架构


整个RecyclerView体系包含三大组件:

这三大组件各司其职,而RecyclerView负责管理,就组成了整个RecyclerView的架构。

3.1 LayoutManager

LayoutManager需要负责以下几部分的工作:

3.2 Adapter

Adapter需要负责以下几部分的工作:

四、ViewHolder的生命周期

4.1 LayoutManager请求RecyclerView提供指定positionView

ViewHolder是和View相绑定的,同时它也是整个复用框架的跟踪单元。在RecyclerView体系中,对ViewHolder采用了二级缓存,分为CacheRecycled Pool,当LayoutManagerRecyclerView请求位于某个PositionView时,Recycled View会先去Cache中寻找,如果找到,那么直接返回;如果找不到,那么再去Recycled Pool中寻找,下面就是整个寻找过程的几种情况:

4.2 LayoutManager找到对应位置的View

LayoutManager通过addView方法把之前找到的View添加进RecyclerViewRecyclerView通过onViewAttachToWindow(VH viewHolder)方法,通知Adapter这个viewHolder所关联的itemView已经被添加到了布局当中,

4.3 LayoutManager请求RecyclerView移除某一个位置的View

4.3.1 普通情况

LayoutManager发现不再需要某一个positionView时,它会通知RecyclerViewRecyclerView通过onViewDetachFromWindow(VH viewHolder)通知Adapter和它绑定的itemView被移出了。同时,RecyclerView判断它是否能够被缓存,假设能够被缓存,那么它会先被放到Cache当中,在Cache中又会判断它内部是否有需要转移到Recycled Pool中的ViewHolder,在放入之后回收池后,通过onViewRecycled(VH viewHolder)方法通知Adapter它被回收了。

4.3.2 特殊情况

在上面的普通的情况中,onViewDetachFromWindow(VH viewHolder)是立即被回调的。然而在实际当中,由于我们需要对View的添加、删除做一些过度动画,这时候,我们需要等待ItemAnimator进行完动画操作之后,才做detachrecycle的逻辑,这一过程对于LayoutManager是不可见的。

4.4 ViewHolder的销毁

在一般情况下,我们不会去销毁ViewHolder,而是把它放入到缓存当中,除非出现以下两种情况。

4.4.1 ViewHolder所绑定的itemView当前状态异常

在放入Recycled Pool时,会去检查itemView的状态是否正常。这一操作的目的主要是为了避免出现诸如此类的情况:当前itemView正在执行动画,此时它可能呈现半透明的状态,如果此时把它放入到回收池中,那么当另一个位置的position需要复用它时就可能会出现问题。
当出现上面的情况后,Recycled Pool会先通过AdapteronFailedToRecycled(VH viewHolder)告诉它我们现在出现了异常的情况,由Adapter的实现者通过返回值来决定是否仍然要把它放入到Recycled Pool,默认是返回false,也就是不放入,那么这个ViewHolder就会被销毁了。

4.4.2 Recycled Pool中已经没有足够的空间

Recycled Pool的空间并不是无限大的,因此,如果没有足够的空间存放要被回收的ViewHolder,那么它也会被销毁。


造成这种情况的一般是动画引起的,例如,我们调用了notifyItemRangeChanged(0, getItemCount())方法,这时候为了进行渐出渐进的动画,那么我们就需要创建两倍的ViewHolder,出现这种情况时一般有两种解决方法:

五、ItemAnimator

对于Item的动画,主要有以下几种情况:

RecyclerView对于动画的处理采用了Predictive的方式,除了当前已经在RecyclerView布局中的View(实线框部分),它还需要知道在屏幕意外的信息(虚线框部分),这样在H被删除的时候,它才能够对J-K进行上移动画,并把原来不在屏幕内的L上移到可视范围之内。

六、ChildHelperAdapterHelper

6.1 ChildHelper

对于ChildHelper的作用是:Provide a virtual children list to layoutmanager,下面我们就首先看一下为什么需要它。

6.1.1 解决什么问题

我们看下面这种情况,假如LayoutManager想要移除一个View,而ItemAnimator又希望给这一移除的操作增加一个动画,那么这时候就会产生冲突,到底应该怎么办,为此,RecyclerView通过ChildHelper来把它们隔离开。

6.1.2 解决问题的方法

RecyclerView收到LayoutManager要求改变布局的请求时,它并不是直接去更改ViewGroup,而是让ChildHelperItemAnimator去协调,并由它来操作ViewGroup


最明显的例子是,假如我们当前列表中状态为0,1,2,3,此时我们移除了position=0Item,这时候假如删除的动画还没有完成,那么LayoutManagerRecyclerViewgetChildAt(0)返回值将会不同,因为在LayoutManager并不清楚ChildHelper的存在,在它看来,position=0Item已经被移除了。
layoutManager.getChildAt(0); //return 1;
recyclerView.getChildAt(0); //return 0;

6.2 AdapterHelper

AdapterHelper所解决的问题和ChildHelper类似,ChildHelper是处理View的,而AdapterHelper用来跟踪ViewHolder的,其作用为:

说起来可能比较抽象,我们用下面这种图理解一下,当我们移动某个Item并且它的onLayout方法还没有完成,那么AdapterLayoutpostion是不相同的:

七、ItemDecoration

ItemDecoration用来在RecyclerViewCanvas上进行额外的绘制操作,我们不仅可以在单个Item(例如给每个Item添加分割线)的Canvas上进行绘制,也可以在整个RecyclerViewCanvas上进行绘制,此外,我们还可以指定Item之间的间隔:

需要注意的点:

参考文章:Android RecyclerView 使用完全解析 体验艺术般的控件

八、RecycledViewPool

RecyclerViewPool用来缓存那些回收的View,这些缓存不仅可以提供给单个RecyclerView使用,还可以提供和别的自定义控件共享。

九、ItemTouchHelper

之前使用ListView的时候,如果需要支持侧滑删除、拖动排序这种操作,那么我们一般用引入一些开源库,现在RecyclerView已经帮我们提供了实现的接口,通过重写ItemTouchHelper的方法,就可以实现上面提到的那些操作。

参考文章:RecyclerView 进阶:使用 ItemTouchHelper 实现拖拽和侧滑删除

十、Tips

public void onBindViewHolder(final ViewHolder, final int position) {
    holder.itemView.setOnClickListener(new View.onClickListener) {
        @Override
        public void onClick(View view) {
            removeAtPostion(position);
        }
    }
}

由于Item会被添加、删除、移动,因此,我们在onBindViewHolder中获得位置,并不一定是当前的位置,例如像下面这样:

onBindViewHolder(holder, 5);
notifyItemMoved(5, 15);
holder.itemView.callOnClick();

那么就会得到错误的位置,这时候应当使用holder.getAdapterPostion()来保证能够得到预期的结果。


更多文章,欢迎访问我的 Android 知识梳理系列:

上一篇下一篇

猜你喜欢

热点阅读