几个概念

  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. ListView异步加载图片,完美实现图文混排

    昨天参加一个面试,面试官让当场写一个类似于新闻列表的页面,文本数据和图片都从网络上获取,想起我还没写过ListView异步加载图片并实现图文混排效果的文章,so,今天就来写一下,介绍一下经验. Lis ...

  2. 【processing】小代码3

    鼠标响应: mouseX, mouseY 鼠标的坐标 ---------------------------------------------- void setup() { size(,); sm ...

  3. 【XLL 框架库函数】 debugPrintf

    通过调用 Windows SDK 函数 OutputDebugStringA 在激活的调试器中输出字符串信息.如果应用程序没有调试器,那么系统调试器就会显示字符串.如果这两种调试器都没使用的话,deb ...

  4. java课后作业6

    一.运行TestInherits.java 结论:通过super调用基类构造方法,必须是子类构造方法中的第一个语句. 二.为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不 ...

  5. endnote设置文献第二行悬挂缩进办法

    参考:http://blog.sina.com.cn/s/blog_62b13cf201014lfr.html  使用[endnote]插入文献后,如果文献稍长些,有第二行,第二行会顶格开始.并且这个 ...

  6. iOS开发人员不容错过的10大工具

    内容简介 1.iOS简介 2.iOS开发十大实用工具之开发环境 3.iOS开发十大实用工具之图标设计 4.iOS开发十大实用工具之原型设计 5.iOS开发十大实用工具之演示工具 6.iOS开发十大实用 ...

  7. 设计模式之Singleton

    class Singleton { private Singleton() { } private static Singleton instance; // v0.1 // public stati ...

  8. Ionic2 Tutorial

    build your first app Now that you have Ionic and its dependencies installed, you can build your firs ...

  9. C语言中do...while(0)的妙用

    在linux内核代码中,经常看到do...while(0)的宏,do...while(0)有很多作用,下面举出几个: 1.避免goto语句: 通常,如果一个函数开始要分配一些资源,然后如果在中途遇到错 ...

  10. MVC4 自定义错误页面(三)

    一.概述 MVC4框架自带了定义错误页,该页面位于Shared/Error,该页面能够显示系统未能捕获的异常,如何才能使用该页面: 二.使用步骤: 1.配置WebConfig文件,在System.We ...