ListView 绘制流程解析

本文详细解析了ListView的绘制流程,包括RecycleBin的数据结构及其在ActiveViews和ScrapViews之间的转换,以及Measure、Layout和Draw过程。重点讨论了RecycleBin如何回收和复用ItemView,减少内存分配,提高性能。
摘要由CSDN通过智能技术生成

目录

LinearLayout绘制流程解析

RelativeLayout绘制流程解析

ListView绘制流程解析

RecyclerView绘制流程解析

絮絮叨叨

从年初到现在,一直积攒了好多知识点没有学习和总结,待办越积越多,此时此刻立下flag,要在这个可爱的八月把所有的待办解决掉。

---- 鲁迅:不,我没说过

背景

在我刚开始学习Android的时候,ListView是列表实现的首选项,现在因为效率等问题慢慢地被RecyclerView替代了,当然,很多简单的业务还是用listview实现更方便。抛开具体现实,光谈理论也是耍流氓的。

为了后面更好的学习RecyclerView,很有必要好好学习了解ListView的绘制机制。

分析

在开始LIstView开始之前,先看下整体重要的数据结构。

AbsListView 是 ListView的父类,也是GridView的父类,当然ListView和GridView都是列表,有很多地方是相似的,因此提取了很多同样的流程到AbsListView中。

AbsListView有一个重要的内部类: RecycleBin

RecycleBin 是 ListView绘制机制的基石,顾名思义,是回收桶的意思。负责专门回收和提供、复用ItemView(ItemView就是ListView中每个滑动的子View)。

RecycleBin

看RecycleBin的注释有这么一段说明它的作用

/**
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
* start of a layout. By construction, they are displaying current information. At the end of
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
* could potentially be used by the adapter to avoid allocating views unnecessarily.
**/

简单来说,RecycleBin负责回收复用view,其中有两个复用存储空间:

一个是ActiveView负责回收当前界面的活跃的View,他们保留着当前显示的信息。另外一个ScrapViews,ActiveView当被滑出屏幕会被回收到到ScrapViews,ScrapViews里面的View可以被复用,重新绑定,这样可以避免listView的Adpater重新生成新的View。

翻译一下,就是ActiveViews是负责保存当前屏幕活跃的View,而ScrapViews是保存滑出屏幕非活跃的View。

二者的区别有一个在于,ActiveViews取出来的View可以直接拿来绘制显示,不需要重新绑定,就是调用Adapter的onCreateView(其中,onCreateView具备了创建View和绑定View的逻辑,而RecyclerView将二者分离开了),而ScrapViews取出来的需要重新绑定。

RecyleBin的数据结构
 private View[] mActiveViews = new View[0];
// 缓存当前界面显示的子View
 
 private ArrayList<View>[] mScrapViews;
 private ArrayList<View> mCurrentScrap;
 // 存储已经废弃的View,mCurrentScrap在viewTypeCount为1下使用,mScrapViews数组对应viewTypeCount种view
 
 private int mViewTypeCount;
// View种类数量,对应Adapter的getItemType()

 private ArrayList<View> mSkippedScrap;
 
 private SparseArray<View> mTransientStateViews; 
 private LongSparseArray<View> mTransientStateViewsById;
// mTransientStateViews/mTransientStateViewsById 缓存Transient瞬态的view,若hasStableId=true,则key为itemId
 

上面其中的mTransientStateViews缓存的是transient状态的view,transient表示瞬间临时状态,通过View的hasTransientState函数来判断,当View处理动画时候,会处于transient状态。

void fillActiveViews(int childCount, int firstActivePosition);
// 将ListView指定元素存储到mActiveViews

View getActiveView(int position)
// 从ActiveViews中取出,但只能取一次

void addScrapView(View scrap, int position)
// 添加ScrapView

View getScrapView(int position)
// 从ScrapViews中取出

void setViewTypeCount(int viewTypeCount)
// 设置ViewTypeCount,子View种类

void scrapActiveViews()
// 将ActiveViews移动到ScrapViews种

在布局之前,如果数据集没有变化,会将显示的子View存到mActivieViews,若数据集发生了变化,将显示的子View添加到mScrapViews,布局完成之后,将mActiveViews剩下的View放到mScrapViews中。

获取子View过程中,如果数据集没有变化,则从mActiveViews直接取出布局子View,若数据集发生了变化,则从mScrapViews中获取,如果匹配到了会重新绑定,如果没匹配到,则创建一个子View绑定。

RecyleBin的大部分的方法都是从上述中获取出View或者存储View的逻辑,在后面源码的过程中再好好具体学习分析。

Measure

之前有分析了ViewGroup/View的绘制流程,其中三个最重要的流程:

measure 测量、layout 布局、draw 绘制

按照这个流程继续来学习ListView,Measure的流程相对比较简单。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int childWidth = 0;
    int childHeight = 0;
    int childState = 0;
    mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
            || heightMode == MeasureSpec.UNSPECIFIED)) {
   
    // 存在子View,无法确定高度时候
        final View child = obtainView(0, mIsScrap);
        // Lay out child directly against the parent measure spec so that
        // we can obtain exected minimum width and height.
        measureScrapChild(child, 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值