几个概念

  1. RecyclerView是一个ViewGroup;
  2. LayoutManager控制RecyclerView的ChildView的布局显示,childview由Recycler提供以及管理;
  3. Recycler具有两级缓存,Scrap和RecycledViewPool,通过Detach以及Remove,对Viewholder进行转移以及状态改变;
  4. RecycledViewPool可以由多个RecyclerView共享;
  5. ViewHolder具有多种状态标记;

关于Recycler

Scrap中的ViewHolder,不用通过Adapter重新处理,只需要attach后回到LayoutManager就可以重用。

RecycledViewPool中的ViewHolder,数据往往是错误的,则需要通过Adapter重新绑定正确的数据后在回到LayoutManager。

当LayoutManager需要一个新的View时,Recycler会行检查scrap中是否有合适的ViewHolder,如果有直接返回给LayoutManager使用;如果没有,就需要从Pool里面寻找,然后右Adapter重新绑定数据后,返回到LayoutManager;如果pool还是没有,就需要由Adapter创建一个新的Viewholder。见如下代码:

  1. View getViewForPosition(int position, boolean dryRun) {
  2.             if (position < 0 || position >= mState.getItemCount()) {
  3.                 throw new IndexOutOfBoundsException("Invalid item position " + position
  4.                         + "(" + position + "). Item count:" + mState.getItemCount());
  5.             }
  6.             boolean fromScrap = false;
  7.             ViewHolder holder = null;
  8.             // 0) If there is a changed scrap, try to find from there
  9.             if (mState.isPreLayout()) {
  10.                 holder = getChangedScrapViewForPosition(position);
  11.                 fromScrap = holder != null;
  12.             }
  13.             // 1) Find from scrap by position
  14.             if (holder == null) {
  15.                 holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
  16.                 if (holder != null) {
  17.                     if (!validateViewHolderForOffsetPosition(holder)) {
  18.                         // recycle this scrap
  19.                         if (!dryRun) {
  20.                             // we would like to recycle this but need to make sure it is not used by
  21.                             // animation logic etc.
  22.                             holder.addFlags(ViewHolder.FLAG_INVALID);
  23.                             if (holder.isScrap()) {
  24.                                 removeDetachedView(holder.itemView, false);
  25.                                 holder.unScrap();
  26.                             } else if (holder.wasReturnedFromScrap()) {
  27.                                 holder.clearReturnedFromScrapFlag();
  28.                             }
  29.                             recycleViewHolderInternal(holder);
  30.                         }
  31.                         holder = null;
  32.                     } else {
  33.                         fromScrap = true;
  34.                     }
  35.                 }
  36.             }
  37.             if (holder == null) {
  38.                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
  39.                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
  40.                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
  41.                             + "position " + position + "(offset:" + offsetPosition + ")."
  42.                             + "state:" + mState.getItemCount());
  43.                 }
  44.  
  45.                 final int type = mAdapter.getItemViewType(offsetPosition);
  46.                 // 2) Find from scrap via stable ids, if exists
  47.                 if (mAdapter.hasStableIds()) {
  48.                     holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
  49.                     if (holder != null) {
  50.                         // update position
  51.                         holder.mPosition = offsetPosition;
  52.                         fromScrap = true;
  53.                     }
  54.                 }
  55.                 if (holder == null && mViewCacheExtension != null) {
  56.                     // We are NOT sending the offsetPosition because LayoutManager does not
  57.                     // know it.
  58.                     final View view = mViewCacheExtension
  59.                             .getViewForPositionAndType(this, position, type);
  60.                     if (view != null) {
  61.                         holder = getChildViewHolder(view);
  62.                         if (holder == null) {
  63.                             throw new IllegalArgumentException("getViewForPositionAndType returned"
  64.                                     + " a view which does not have a ViewHolder");
  65.                         } else if (holder.shouldIgnore()) {
  66.                             throw new IllegalArgumentException("getViewForPositionAndType returned"
  67.                                     + " a view that is ignored. You must call stopIgnoring before"
  68.                                     + " returning this view.");
  69.                         }
  70.                     }
  71.                 }
  72.                 if (holder == null) { // fallback to recycler
  73.                     // try recycler.
  74.                     // Head to the shared pool.
  75.                     if (DEBUG) {
  76.                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
  77.                                 + "pool");
  78.                     }
  79.                     holder = getRecycledViewPool()
  80.                             .getRecycledView(mAdapter.getItemViewType(offsetPosition));
  81.                     if (holder != null) {
  82.                         holder.resetInternal();
  83.                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
  84.                             invalidateDisplayListInt(holder);
  85.                         }
  86.                     }
  87.                 }
  88.                 if (holder == null) {
  89.                     holder = mAdapter.createViewHolder(RecyclerView.this,
  90.                             mAdapter.getItemViewType(offsetPosition));
  91.                     if (DEBUG) {
  92.                         Log.d(TAG, "getViewForPosition created new ViewHolder");
  93.                     }
  94.                 }
  95.             }
  96.             boolean bound = false;
  97.             if (mState.isPreLayout() && holder.isBound()) {
  98.                 // do not update unless we absolutely have to.
  99.                 holder.mPreLayoutPosition = position;
  100.             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
  101.                 if (DEBUG && holder.isRemoved()) {
  102.                     throw new IllegalStateException("Removed holder should be bound and it should"
  103.                             + " come here only in pre-layout. Holder: " + holder);
  104.                 }
  105.                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
  106.                 mAdapter.bindViewHolder(holder, offsetPosition);
  107.                 attachAccessibilityDelegate(holder.itemView);
  108.                 bound = true;
  109.                 if (mState.isPreLayout()) {
  110.                     holder.mPreLayoutPosition = position;
  111.                 }
  112.             }
  113.  
  114.             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
  115.             final LayoutParams rvLayoutParams;
  116.             if (lp == null) {
  117.                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
  118.                 holder.itemView.setLayoutParams(rvLayoutParams);
  119.             } else if (!checkLayoutParams(lp)) {
  120.                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
  121.                 holder.itemView.setLayoutParams(rvLayoutParams);
  122.             } else {
  123.                 rvLayoutParams = (LayoutParams) lp;
  124.             }
  125.             rvLayoutParams.mViewHolder = holder;
  126.             rvLayoutParams.mPendingInvalidate = fromScrap && bound;
  127.             return holder.itemView;
  128.         }

关于ViewHolder

在RecyclerView里面,view是有多重状态的,各种状态在ViewHolder里面定义。看看下面的代码:

  1. public static abstract class ViewHolder {
  2.       public final View itemView;
  3.       int mPosition = NO_POSITION;
  4.       int mOldPosition = NO_POSITION;
  5.       long mItemId = NO_ID;
  6.       int mItemViewType = INVALID_TYPE;
  7.       int mPreLayoutPosition = NO_POSITION;
  8.  
  9.       // The item that this holder is shadowing during an item change event/animation
  10.       ViewHolder mShadowedHolder = null;
  11.       // The item that is shadowing this holder during an item change event/animation
  12.       ViewHolder mShadowingHolder = null;
  13.  
  14.       /**
  15.        * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
  16.        * are all valid.
  17.        */
  18.       static final int FLAG_BOUND = 1 << 0;
  19.  
  20.       /**
  21.        * The data this ViewHolder's view reflects is stale and needs to be rebound
  22.        * by the adapter. mPosition and mItemId are consistent.
  23.        */
  24.       static final int FLAG_UPDATE = 1 << 1;
  25.  
  26.       /**
  27.        * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
  28.        * are not to be trusted and may no longer match the item view type.
  29.        * This ViewHolder must be fully rebound to different data.
  30.        */
  31.       static final int FLAG_INVALID = 1 << 2;
  32.  
  33.       /**
  34.        * This ViewHolder points at data that represents an item previously removed from the
  35.        * data set. Its view may still be used for things like outgoing animations.
  36.        */
  37.       static final int FLAG_REMOVED = 1 << 3;
  38.  
  39.       /**
  40.        * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
  41.        * and is intended to keep views around during animations.
  42.        */
  43.       static final int FLAG_NOT_RECYCLABLE = 1 << 4;
  44.  
  45.       /**
  46.        * This ViewHolder is returned from scrap which means we are expecting an addView call
  47.        * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
  48.        * the end of the layout pass and then recycled by RecyclerView if it is not added back to
  49.        * the RecyclerView.
  50.        */
  51.       static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
  52.  
  53.       /**
  54.        * This ViewHolder's contents have changed. This flag is used as an indication that
  55.        * change animations may be used, if supported by the ItemAnimator.
  56.        */
  57.       static final int FLAG_CHANGED = 1 << 6;
  58.  
  59.       /**
  60.        * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
  61.        * it unless LayoutManager is replaced.
  62.        * It is still fully visible to the LayoutManager.
  63.        */
  64.       static final int FLAG_IGNORE = 1 << 7;

------EOF----------

RecyclerView 介绍 02 – 重要概念的更多相关文章

  1. C#多线程之旅(1)——介绍和基本概念

    原文地址:C#多线程之旅(1)——介绍和基本概念 C#多线程之旅目录: C#多线程之旅(1)——介绍和基本概念 C#多线程之旅(2)——创建和开始线程 C#多线程之旅(3)——线程池 C#多线程之旅( ...

  2. TensorFlow入门,基本介绍,基本概念,计算图,pip安装,helloworld示例,实现简单的神经网络

    TensorFlow入门,基本介绍,基本概念,计算图,pip安装,helloworld示例,实现简单的神经网络

  3. 01.课程介绍 & 02.最小可行化产品MVP

    01.课程介绍 02.最小可行化产品MVP 产品开发过程 最小化和可用之间找到一个平衡点

  4. vue项目搭建介绍02

    目录 vue项目搭建介绍02 python-pycharm设置: vue创建项目分类: vue-cli构建 自定义构建 基础的vue项目目录: vue项目搭建介绍02 python-pycharm设置 ...

  5. ZooKeeper入门实战教程(一)-介绍与核心概念

    1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...

  6. 黑马_13 Spring Boot:01.spring boot 介绍&&02.spring boot 入门

    13 Spring Boot: 01.spring boot 介绍&&02.spring boot 入门 04.spring boot 配置文件 SpringBoot基础 1.1 原有 ...

  7. 083 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 02 构造方法-带参构造方法

    083 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 02 构造方法-带参构造方法 本文知识点:构造方法-带参构造方法 说明:因为时间紧张, ...

  8. Android新组件RecyclerView介绍,其效率更好

    今天我们首先来说为什么要介绍这个新组件RecyclerView,因为前几天我发布了一个常用面试题ListView的复用及如何优化的文章,介绍给一些开发者,但是我看到有关的反馈说:现在都不再用listv ...

  9. maven使用.02.一些概念

    在上一篇POST中,简要的介绍了一下maven的特点,优势,安装.并建立了一个简单地Hello world工程.这一篇POST中,将主要会介绍一下Maven的一些约定. pom.xml文件 Maven ...

随机推荐

  1. Maven实现直接部署Web项目到Tomcat7(转)

    转载自:http://my.oschina.net/angel243/blog/178554 以前在项目中很少使用Maven,最近自己学习了一下maven,真的是非常强大的项目构建工具,对于依赖包的定 ...

  2. 【mysql】利用Navicat for MySQL的使用

    1. 查看sql语句 如果忘记了某个SQL语句怎么写,可以利用Navicat for MySQL的历史日志来查看 在Navicat for MySQL中,直接对数据库进行想要的操作,然后点击工具-&g ...

  3. cocos2d-x 第二篇 HelloWorld的流程

    这篇博客主要是带领大家一起了解整个游戏的执行过程,其中涉及的一些譬如导演,场景,层之类的概念将会在后面讲解. 看main函数的区别: #import <UIKit/UIKit.h> // ...

  4. python数据库(mysql)操作

    http://fantefei.blog.51cto.com/2229719/1282443

  5. Centos7 ZooKeeper 安装过程

    www.apache.org/dist/上可以下载Hadoop整个生态环境的组件,我下的Zookeeper3.4.6版本 我一般都是在一个虚拟机上将一.二步都做完,然后克隆出来,再到克隆出来的虚拟机上 ...

  6. python基础——使用@property

    python基础——使用@property 在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9 ...

  7. Vi 的基本使用

    一.Vi入门  Unix 提供了全屏幕的Vi编辑器,这使我们的工作轻松不少.不少DOS用户抱怨Vi编辑器不象DOS下的编辑器如edit那么好用,这 是因为Vi考虑到各种用户的需要,没有使用某些通用的编 ...

  8. CNN初步-1

    Convolution:   个特征,则这时候把输入层的所有点都与隐含层节点连接,则需要学习10^6个参数,这样的话在使用BP算法时速度就明显慢了很多. 所以后面就发展到了局部连接网络,也就是说每个隐 ...

  9. Win10开机提示Resume from Hibernation该怎么办?

    Windows10系统的电脑开机提示:Resume from Hibernation(从休眠恢复),这是电脑没有真正关机,而是上次关机时进入了[休眠状态],所以开机时提示:从休眠恢复.如何解决Wind ...

  10. 无废话ExtJs 入门教程十[单选组:RadioGroup、复选组:CheckBoxGroup]

    无废话ExtJs 入门教程十[单选组:RadioGroup.复选组:CheckBoxGroup] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在表单里加了个一个单选组,一个复 ...