ListView原理分析之重要方法介绍

ListView直接继承自的AbsListView,而AbsListView有两个子实现类,一个是ListView,另一个就是GridView,

因此我们从这一点就可以猜出来,ListView和GridView在工作原理和实现上都是有很多共同点的。然后AbsListView

又继承自AdapterView,AdapterView继承自ViewGroup,后面就是我们所熟知的了。


Adapter

  说道ListView就会想到adapter,经常看到它们配套使用,为什么要使用adapter呢?

1、适配多种类型数据

控件就是为了交互和展示数据,ListView控件,它比较特殊,可以展示多种类型的数据交互和显示,比如array,list,Cursor

或者Object,各种自定义类型,要实现多类型数据交互,要是都在View中处理,就不太灵活,adapter就充当了数据转换的桥梁。

2、重用机制RecycleBin

不管你有千条万条,ListView通过重用机制,减少了内存开销,及读xml的次数,可以说这才是ListView的精髓。



如上图所示:频幕中可显示6个activit的Item,向上滑动,Item1滑出屏幕,这时Item1被缓存起来,当Item7要出

现在屏幕时,就去缓存中,找到已经缓存好的view,刷新数据。这样不管你有多少条数据,也只是对这第一次

建立的6个Item View重用。


既然这么强大,我们就来分析下RecycleBin

比较重要的几个成员变量

mActiveViews          // View[ ]  存放当前可见View,也就是上图6个可见的Item

mCurrentScrap       // ArrayList<View> 存放废弃的View,也就是当Item1滑出屏幕后,就被添加到这个list中了

mScrapViews        // ArrayList<View>[ ]  存放废弃的Views,这个数组是在多类型布局中用到,与它有关的变

                                  量ViewTypeCount,在adapter使用了getViewTypeCount() 后,会把View缓存到这个数组中


几个重要的方法:

  • fillActiveViews() 这个方法接收两个参数,第一个参数表示要存储的view的数量,第二个参数表示ListView中第一个可见元素的position值。RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews数组当中。

 

  /**
         * Fill ActiveViews with all of the children of the AbsListView.
         *
         * @param childCount The minimum number of views mActiveViews should hold
         * @param firstActivePosition The position of the first view that will be stored in
         *        mActiveViews
         */
        void fillActiveViews(int childCount, int firstActivePosition) {
            if (mActiveViews.length < childCount) {
                mActiveViews = new View[childCount];
            }
            mFirstActivePosition = firstActivePosition;

            //noinspection MismatchedReadAndWriteOfArray
            final View[] activeViews = mActiveViews;
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
                // Don't put header or footer views into the scrap heap
                if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                    // Note:  We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
                    //        However, we will NOT place them into scrap views.
                    activeViews[i] = child;
                }
            }
        }



  • getActiveView() 这个方法和fillActiveViews()是对应的,用于从mActiveViews数组当中获取数据。该方法接收一个position参数,表示元素在ListView当中的位置,方法内部会自动将position值转换成mActiveViews数组对应的下标值。需要注意的是,mActiveViews当中所存储的View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews不能被重复利用。

  View getActiveView(int position) {
            int index = position - mFirstActivePosition;
            final View[] activeViews = mActiveViews;
            if (index >=0 && index < activeViews.length) {
                final View match = activeViews[index];
               <span style="color:#3333FF;"> activeViews[index] = null;</span>
                return match;
            }
            return null;
        }


  • addScrapView() 用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),就应该调用这个方法来对View进行缓存,RecycleBin当中使用mScrapViews和mCurrentScrap这两个List来存储废弃View。

   if (scrapHasTransientState) {
           // 快速滑动过渡状态处理,这里不分析
   } else {
                if (mViewTypeCount == 1) {
                    mCurrentScrap.add(scrap);
                } else {
                    mScrapViews[viewType].add(scrap);
                }

                if (mRecyclerListener != null) {
                    mRecyclerListener.onMovedToScrapHeap(scrap);
                }
            }


  • getScrapView 用于从废弃缓存中取出一个View,这些废弃缓存中的View是没有顺序可言的,因此getScrapView()方法中的算法也非常简单,就是直接从mCurrentScrap当中获取尾部的一个scrap view进行返回。

       

    View getScrapView(int position) {
            if (mViewTypeCount == 1) {
                return retrieveFromScrap(mCurrentScrap, position);
            } else {
                final int whichScrap = mAdapter.getItemViewType(position);
                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                }
            }
            return null;
        }


  • setViewTypeCount() 我们都知道Adapter当中可以重写一个getViewTypeCount()来表示ListView中有几种类型的数据项,而setViewTypeCount()方法的作用就是为每种类型的数据项都单独启用一个RecycleBin缓存机制。实际上,getViewTypeCount()方法通常情况下使用的并不是很多,所以我们只要知道RecycleBin当中有这样一个功能就行了。

   

  public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
            }
            //noinspection unchecked
            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
            for (int i = 0; i < viewTypeCount; i++) {
                scrapViews[i] = new ArrayList<View>();
            }
            mViewTypeCount = viewTypeCount;
            mCurrentScrap = scrapViews[0];
            mScrapViews = scrapViews;
        }
         setViewTypeCount()赋值实在adapter中调用getViewTypeCount()后得到ViewTypeCount。


说道这,都了解了listView是怎么缓存的了吧,具体怎么绘制就不多说了,都是onMeasure()用于测量View的大小,onLayout()用于确定View的布局,

onDraw()用于将View绘制到界面上。onMeasureonDraw没什么特别的,需要进一步分析的可以看看ListView的父类AbsListView中实现的onLayout



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值