当前位置: 代码迷 >> Android >> Android ListView的了解(二) - Adapter, BaseAdapter, RecycleBin
  详细解决方案

Android ListView的了解(二) - Adapter, BaseAdapter, RecycleBin

热度:135   发布时间:2016-04-28 07:29:06.0
Android ListView的理解(二) --- Adapter, BaseAdapter, RecycleBin

一、前述

        通常,我们在使用ListView / GridView时,都需要使用Adapter,Adapter有多种,最常用的就是BaseAdapter 和 CursorAdapter了,前者是属于通用的,而后者通常会与数据库一起来使用。

        1. Adapter在源码中是interface,而不是AbstractClass(http://developer.android.com/reference/android/widget/Adapter.html);

        2. BaseAdapter是抽象类,而不是interface;

        3. CursorAdapter也是抽象类,它是继承于BaseAdapter的;

        4. 还有其它Adapter,如ListAdapter等;

        大家看了以上几点后,肯定会有疑问,BaseAdapter与Adapter是怎么联系起来的?

        看过文档或源码的朋友肯定知道:

    /**     * Common base class of common implementation for an [email protected] Adapter} that can be     * used in both [email protected] ListView} (by implementing the specialized     * [email protected] ListAdapter} interface} and [email protected] Spinner} (by implementing the     * specialized [email protected] SpinnerAdapter} interface.     */    public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter

        ListAdapter和SpinnerAdapter是两个interface,它们直接继承于Adapter,BaseAdapter是抽象类,可以选择性的实现接口里定义的方法;

        其实,BaseAdapter也是大多数XXXAdapter(不是接口,而是类或抽象类)的parent,只有少数和BaseAdapter一样,如:HeaderViewListAdapter。

二、Adapter

        2.1 Adapter

public interface Adapter {    /**     * Register an observer that is called when changes happen to the data used by this adapter.     *     * @param observer the object that gets notified when the data set changes.     */    void registerDataSetObserver(DataSetObserver observer);    /**     * Unregister an observer that has previously been registered with this     * adapter via [email protected] #registerDataSetObserver}.     *     * @param observer the object to unregister.     */    void unregisterDataSetObserver(DataSetObserver observer);    /**     * How many items are in the data set represented by this Adapter.     *      * @return Count of items.     */    int getCount();           /**     * Get the data item associated with the specified position in the data set.     *      * @param position Position of the item whose data we want within the adapter's      * data set.     * @return The data at the specified position.     */    Object getItem(int position);        /**     * Get the row id associated with the specified position in the list.     *      * @param position The position of the item within the adapter's data set whose row id we want.     * @return The id of the item at the specified position.     */    long getItemId(int position);        /**     * Indicates whether the item ids are stable across changes to the     * underlying data.     *      * @return True if the same id always refers to the same object.     */    boolean hasStableIds();        /**     * Get a View that displays the data at the specified position in the data set. You can either     * create a View manually or inflate it from an XML layout file. When the View is inflated, the     * parent View (GridView, ListView...) will apply default layout parameters unless you use     * [email protected] android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}     * to specify a root view and to prevent attachment to the root.     *      * @param position The position of the item within the adapter's data set of the item whose view     *        we want.     * @param convertView The old view to reuse, if possible. Note: You should check that this view     *        is non-null and of an appropriate type before using. If it is not possible to convert     *        this view to display the correct data, this method can create a new view.     *        Heterogeneous lists can specify their number of view types, so that this View is     *        always of the right type (see [email protected] #getViewTypeCount()} and     *        [email protected] #getItemViewType(int)}).     * @param parent The parent that this view will eventually be attached to     * @return A View corresponding to the data at the specified position.     */    View getView(int position, View convertView, ViewGroup parent);    /**     * An item view type that causes the [email protected] AdapterView} to ignore the item     * view. For example, this can be used if the client does not want a     * particular view to be given for conversion in     * [email protected] #getView(int, View, ViewGroup)}.     *      * @see #getItemViewType(int)     * @see #getViewTypeCount()     */    static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;        /**     * Get the type of View that will be created by [email protected] #getView} for the specified item.     *      * @param position The position of the item within the adapter's data set whose view type we     *        want.     * @return An integer representing the type of View. Two views should share the same type if one     *         can be converted to the other in [email protected] #getView}. Note: Integers must be in the     *         range 0 to [email protected] #getViewTypeCount} - 1. [email protected] #IGNORE_ITEM_VIEW_TYPE} can     *         also be returned.     * @see #IGNORE_ITEM_VIEW_TYPE     */    int getItemViewType(int position);        /**     * <p>     * Returns the number of types of Views that will be created by     * [email protected] #getView}. Each type represents a set of views that can be     * converted in [email protected] #getView}. If the adapter always returns the same     * type of View for all items, this method should return 1.     * </p>     * <p>     * This method will only be called when when the adapter is set on the     * the [email protected] AdapterView}.     * </p>     *      * @return The number of types of Views that will be created by this adapter     */    int getViewTypeCount();        static final int NO_SELECTION = Integer.MIN_VALUE;      /**      * @return true if this adapter doesn't contain any data.  This is used to determine      * whether the empty view should be displayed.  A typical implementation will return      * getCount() == 0 but since getCount() includes the headers and footers, specialized      * adapters might want a different behavior.      */     boolean isEmpty();}

        这里面只定义了方法,其中:

    void registerDataSetObserver(DataSetObserver observer);    void unregisterDataSetObserver(DataSetObserver observer);

        这两个方法我们通常不需要太关心,因为ListView, GridView会自动去注册/取消注册的,DataSetObserver用的是一个观察者模式,目的就是当Adapter中的数据发生变化是,能够通知ListView 或 GridView。

    int getCount();       Object getItem(int position);    long getItemId(int position);    View getView(int position, View convertView, ViewGroup parent);

       当我们继承BaseAdapter时,通常需要实现以上四个方法,这四个方法大家应该用的很熟了,这里我就不说了;

    int getItemViewType(int position);    int getViewTypeCount();

       特别要注意的是这两个方法,通常在BaseAdapter里面,默认是0和1,那我们在什么时候会需要override这两个方法呢?

       我们知道Adapter.getView中有个convertView,是用来复用View的,如果数据要进行分组,比如按照手机中的联系人按ABCD....来分类,那么,这个Listview就有两种view,一种是分组标签,另一种就是联系人,因此,我们需要设置ViewTypeCount为2,同时将分组view和联系人view设置不同的view type,这样,复用时,就能区分开来,有效的节省内存。

        2.2 ListAdapter

public interface ListAdapter extends Adapter {    /**     * Indicates whether all the items in this adapter are enabled. If the     * value returned by this method changes over time, there is no guarantee     * it will take effect.  If true, it means all items are selectable and     * clickable (there is no separator.)     *      * @return True if all items are enabled, false otherwise.     *      * @see #isEnabled(int)      */    public boolean areAllItemsEnabled();    /**     * Returns true if the item at the specified position is not a separator.     * (A separator is a non-selectable, non-clickable item).     *      * The result is unspecified if position is invalid. An [email protected] ArrayIndexOutOfBoundsException}     * should be thrown in that case for fast failure.     *     * @param position Index of the item     *      * @return True if the item is not a separator     *      * @see #areAllItemsEnabled()      */    boolean isEnabled(int position);}

        这里面的方法就两个,一个是判断在Adapter中所有的item可选择或可点击,另一个是指点位置的item可选择或可点击,没啥可讲的;

        2.3 SpinnerAdapter

        这个是给下拉列表控件用的,就一个方法getDropDownView,其它没啥好讲的;

        2.4 BaseAdapter

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {    private final DataSetObservable mDataSetObservable = new DataSetObservable();    public boolean hasStableIds() {        return false;    }        public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }    public void unregisterDataSetObserver(DataSetObserver observer) {        mDataSetObservable.unregisterObserver(observer);    }        /**     * Notifies the attached observers that the underlying data has been changed     * and any View reflecting the data set should refresh itself.     */    public void notifyDataSetChanged() {        mDataSetObservable.notifyChanged();    }    /**     * Notifies the attached observers that the underlying data is no longer valid     * or available. Once invoked this adapter is no longer valid and should     * not report further data set changes.     */    public void notifyDataSetInvalidated() {        mDataSetObservable.notifyInvalidated();    }    public boolean areAllItemsEnabled() {        return true;    }    public boolean isEnabled(int position) {        return true;    }    public View getDropDownView(int position, View convertView, ViewGroup parent) {        return getView(position, convertView, parent);    }    public int getItemViewType(int position) {        return 0;    }    public int getViewTypeCount() {        return 1;    }        public boolean isEmpty() {        return getCount() == 0;    }}

        BaseAdapter也没啥讲的,我们继承时,只用实现它未实现的方法就行了,有需要的话,override一下也行。

三、RecycleBin

        在AbsListView(ListView / GridView)中,View复用全是依赖RecycleBin来缓存用过的View。

        在《Android ListView的理解(一)》中,讲解obtainView方法中,有提到过该类,不过,只说了如何用,而未说明这个类里面的方法和实现,这次就来分析下。

        3.1 类成员

    /**     * 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.     *     * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)     * @see android.widget.AbsListView.RecyclerListener     */    class RecycleBin {        private RecyclerListener mRecyclerListener;        /**         * The position of the first view stored in mActiveViews.         */        private int mFirstActivePosition;        /**         * Views that were on screen at the start of layout. This array is populated at the start of         * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.         * Views in mActiveViews represent a contiguous range of Views, with position of the first         * view store in mFirstActivePosition.         */        private View[] mActiveViews = new View[0];        /**         * Unsorted views that can be used by the adapter as a convert view.         */        private ArrayList<View>[] mScrapViews;        private int mViewTypeCount;        private ArrayList<View> mCurrentScrap;    }

         区区几个成员变量:

        1. 当发生View回收时,mRecyclerListener若有注册,则会通知给注册者;

        2. mFirstActivePosition对应的是在ListView中,可视区域中的第一个item position(即getFirstVisiblePosition);

        3. mActiveViews存储着当前ListView中,可见区域中的View;

        4. mScrapViews是一个ArrayList,为啥?这个与mViewTypeCount相关,那么大家也就猜到了,mScrapViews缓存着ViewTypeCount种类型的View,默认是1,手机联系人是2等;

        5. mCurrentScrap就是指向当前mScrapViews中的一组,默认ViewTypeCount = 1的情况下,mCurrentScrap = mScrapViews[0];

        3.2 类方法分析

        3.2.1 setViewTypeCount

        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;        }

        设置ViewTypeCount,然后初始化类成员变量;

        3.2.2 markChildrenDirty

        public void markChildrenDirty() {            if (mViewTypeCount == 1) {                final ArrayList<View> scrap = mCurrentScrap;                final int scrapCount = scrap.size();                for (int i = 0; i < scrapCount; i++) {                    scrap.get(i).forceLayout();                }            } else {                final int typeCount = mViewTypeCount;                for (int i = 0; i < typeCount; i++) {                    final ArrayList<View> scrap = mScrapViews[i];                    final int scrapCount = scrap.size();                    for (int j = 0; j < scrapCount; j++) {                        scrap.get(j).forceLayout();                    }                }            }        }

        将mScrapView中回收回来的View设置一样标志,在下次被复用到ListView中时,告诉viewroot重新layout该view

        forceLayout方法

    /**     * Forces this view to be laid out during the next layout pass.     * This method does not call requestLayout() or forceLayout()     * on the parent.     */    public void forceLayout() {        mPrivateFlags |= FORCE_LAYOUT;        mPrivateFlags |= INVALIDATED;    }

         该方法只是设置标志,并不会通知其parent来重新layout。

         3.2.3 shouldRecycleViewType

        public boolean shouldRecycleViewType(int viewType) {            return viewType >= 0;        }

         判断给定的view的viewType指明是否可以回收回。

         那什么情况下,viewType < 0?即不能回收?

         我们在ListView, GridView 或 AbsListView中,找不到小于0的TYPE定义,但是,在AbsListView的parent类AdapterView中找到了:

public abstract class AdapterView<T extends Adapter> extends ViewGroup {    /**     * The item view type returned by [email protected] Adapter#getItemViewType(int)} when     * the adapter does not want the item's view recycled.     */    public static final int ITEM_VIEW_TYPE_IGNORE = -1;    /**     * The item view type returned by [email protected] Adapter#getItemViewType(int)} when     * the item is a header or footer.     */    public static final int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2;		.........}

        上述表明,指定忽略的,或者是 HeaderView / FootView是不被回收的。如有特殊需要可以将自己定义的viewType设置为-1,否则,将会浪费内存,导致OOM,切记!

        3.2.4 clear

        /**         * Clears the scrap heap.         */        void clear() {            if (mViewTypeCount == 1) {                final ArrayList<View> scrap = mCurrentScrap;                final int scrapCount = scrap.size();                for (int i = 0; i < scrapCount; i++) {                    removeDetachedView(scrap.remove(scrapCount - 1 - i), false);                }            } else {                final int typeCount = mViewTypeCount;                for (int i = 0; i < typeCount; i++) {                    final ArrayList<View> scrap = mScrapViews[i];                    final int scrapCount = scrap.size();                    for (int j = 0; j < scrapCount; j++) {                        removeDetachedView(scrap.remove(scrapCount - 1 - j), false);                    }                }            }        }

        这个方法一目了然,清理ScrapView中的View,并将这些View从窗口中Detach。

        3.2.5 fillActiveViews

        /**         * 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;            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;                }            }        }

        该方法就是填充mActiveView数组。当Adapter中的数据个数未发生变化时,此时用户可能只是滚动,或点击等操作,ListView中item的个数会发生变化,因此,需要将可视的item加入到mActiveView中来管理。

        3.2.6 getActiveView

        /**         * Get the view corresponding to the specified position. The view will be removed from         * mActiveViews if it is found.         *         * @param position The position to look up in mActiveViews         * @return The view if it is found, null otherwise         */        View getActiveView(int position) {            int index = position - mFirstActivePosition;            final View[] activeViews = mActiveViews;            if (index >=0 && index < activeViews.length) {                final View match = activeViews[index];                activeViews[index] = null;                return match;            }            return null;        }

        position是adapter中的绝对下标值,mFirstActivePosition前面说过了,是当前可视区域的下标值,对应在adapter中的绝对值,如果找到,则返回找到的View,并将mActiveView对应的位置设置为null。

        对于3.2.5和3.2.6这两个方法,在ListView.layoutChildren或GridView.layoutChildren中调用的,用户可能做了一些操作(未导致个数发生变化),以ListView.layoutChildren为例,代码片断:

    @Override    protected void layoutChildren() {            .........			            // Pull all children into the RecycleBin.            // These views will be reused if possible            final int firstPosition = mFirstPosition;            final RecycleBin recycleBin = mRecycler;            // reset the focus restoration            View focusLayoutRestoreDirectChild = null;            // Don't put header or footer views into the Recycler. Those are            // already cached in mHeaderViews;            if (dataChanged) {                for (int i = 0; i < childCount; i++) {                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);                    if (ViewDebug.TRACE_RECYCLER) {                        ViewDebug.trace(getChildAt(i),                                ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);                    }                }            } else {                recycleBin.fillActiveViews(childCount, firstPosition);            }			            .........			            fillXXX			            .........			            // Flush any cached views that did not get reused above            recycleBin.scrapActiveViews();    }

        上面代码分成了三段:

        step1:如果数据发生变化,就将所有view加入到mScrapView中,否则,将所有view放到mActiveView中;

        step2:添加view到listview中;

        step3:回收mActiveView中的未使用的view到mScrapView中;

        注:在step1中,如果是addScrapView,则所有的view将会detach,如果是fillActiveViews,则不会detach,只有在step3中,未用到的view才会detach。

        3.2.7 retrieveFromScrap(这个不属于RecycleBin类,是属于AbslistView中的方法,不过为了讲之后的方法,这个方法就单独拿出来讲解)

    static View retrieveFromScrap(ArrayList<View> scrapViews, int position) {        int size = scrapViews.size();        if (size > 0) {            // See if we still have a view for this position.            for (int i=0; i<size; i++) {                View view = scrapViews.get(i);                if (((AbsListView.LayoutParams)view.getLayoutParams())                        .scrappedFromPosition == position) {                    scrapViews.remove(i);                    return view;                }            }            return scrapViews.remove(size - 1);        } else {            return null;        }    }

        根据position,从mScrapView中找:

        1. 如果有view.scrappedFromPosition = position的,直接返回该view;

        2. 否则返回mScrapView中最后一个;

        3. 如果缓存中没有view,则返回null;

        下面,我们来分析下这三种情况在什么条件下满足?

         a. 第三种情况,这个最简单:

         一开始,listview稳定后,显示N个,此时mScrapView中是没有缓存view的,当我们向上滚动一小段距离(第一个此时仍显示部分),新的view将会显示,此时listview会调用Adapter.getView,但是缓存中没有,因此convertView是null,所以,我们得分配一块内存来创建新的convertView;

         b. 第二种情况:

         在a中,我们继续向上滚动,直接第一个view完全移出屏幕(假设没有新的item),此时,第一个view就会被detach,并被加入到mScrapView中;然后,我们还继续向上滚动,直接后面又将要显示新的item view时,此时,系统会从mScrapView中找position对应的View,显然,是找不到的,则将从mScrapView中,取最后一个缓存的view传递给convertView;

        c. 第一种情况:

        紧接着在b中(标示为橙色的文字后面),第一个被完全移出,加入到mScrapView中,且没有新增的item到listview中,此时,缓存中就只有第一个view;然后,我此时向下滑动,则之前的第一个item,将被显示出来,此时,从缓存中查找position对应的view有没有,当然,肯定是找到了,就直接返回了。

        以上三种情况分析完毕!大家也理解了吧,没完全理解的,多想想就能想通啦。

        3.2.8 getScrapView

        /**         * @return A view from the ScrapViews collection. These are unordered.         */        View getScrapView(int position) {            if (mViewTypeCount == 1) {                return retrieveFromScrap(mCurrentScrap, position);            } else {                int whichScrap = mAdapter.getItemViewType(position);                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {                    return retrieveFromScrap(mScrapViews[whichScrap], position);                }            }            return null;        }

        getScrapView实际上就是根据viewType,来查找不同的mScrapView,分析了retrieveFromScrap后,getScrapView就很清楚了。

        3.2.9 addScrapView

        /**         * Put a view into the ScapViews list. These views are unordered.         *         * @param scrap The view to add         */        void addScrapView(View scrap, int position) {            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();            if (lp == null) {                return;            }            // Don't put header or footer views or views that should be ignored            // into the scrap heap            int viewType = lp.viewType;            if (!shouldRecycleViewType(viewType)) {                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {                    removeDetachedView(scrap, false);                }                return;            }            lp.scrappedFromPosition = position;            if (mViewTypeCount == 1) {                scrap.dispatchStartTemporaryDetach();                mCurrentScrap.add(scrap);            } else {                scrap.dispatchStartTemporaryDetach();                mScrapViews[viewType].add(scrap);            }            if (mRecyclerListener != null) {                mRecyclerListener.onMovedToScrapHeap(scrap);            }        }

        此方法顾名思义,就是将移出可视区域的view,设置它的scrappedFromPosition,然后从窗口中detach该view,并根据viewType加入到mScrapView中。

        3.2.10 scrapActiveViews

        /**         * Move all views remaining in mActiveViews to mScrapViews.         */        void scrapActiveViews() {            final View[] activeViews = mActiveViews;            final boolean hasListener = mRecyclerListener != null;            final boolean multipleScraps = mViewTypeCount > 1;            ArrayList<View> scrapViews = mCurrentScrap;            final int count = activeViews.length;            for (int i = count - 1; i >= 0; i--) {                final View victim = activeViews[i];                if (victim != null) {                    final AbsListView.LayoutParams lp                            = (AbsListView.LayoutParams) victim.getLayoutParams();                    int whichScrap = lp.viewType;                    activeViews[i] = null;                    if (!shouldRecycleViewType(whichScrap)) {                        // Do not move views that should be ignored                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {                            removeDetachedView(victim, false);                        }                        continue;                    }                    if (multipleScraps) {                        scrapViews = mScrapViews[whichScrap];                    }                    victim.dispatchStartTemporaryDetach();                    lp.scrappedFromPosition = mFirstActivePosition + i;                    scrapViews.add(victim);                    if (hasListener) {                        mRecyclerListener.onMovedToScrapHeap(victim);                    }                    if (ViewDebug.TRACE_RECYCLER) {                        ViewDebug.trace(victim,                                ViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP,                                mFirstActivePosition + i, -1);                    }                }            }            pruneScrapViews();        }

        在分析3.2.5和3.2.6方法时,step3已经提到这个方法了,实际上就是将mActiveView中未使用的view回收(因为,此时已经移出可视区域了)。

        3.2.11 pruneScrapViews

        /**         * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews.         * (This can happen if an adapter does not recycle its views).         */        private void pruneScrapViews() {            final int maxViews = mActiveViews.length;            final int viewTypeCount = mViewTypeCount;            final ArrayList<View>[] scrapViews = mScrapViews;            for (int i = 0; i < viewTypeCount; ++i) {                final ArrayList<View> scrapPile = scrapViews[i];                int size = scrapPile.size();                final int extras = size - maxViews;                size--;                for (int j = 0; j < extras; j++) {                    removeDetachedView(scrapPile.remove(size--), false);                }            }        }

        该方法解释为:mScrapView中每个ScrapView数组大小不应该超过mActiveView的大小,如果超过,系统认为程序并没有复用convertView,而是每次都是创建一个新的view,为了避免产生大量的闲置内存且增加OOM的风险,系统会在每次回收后,去检查一下,将超过的部分释放掉,节约内存降低OOM风险。

        3.2.12 reclaimScrapViews

        /**         * Puts all views in the scrap heap into the supplied list.         */        void reclaimScrapViews(List<View> views) {            if (mViewTypeCount == 1) {                views.addAll(mCurrentScrap);            } else {                final int viewTypeCount = mViewTypeCount;                final ArrayList<View>[] scrapViews = mScrapViews;                for (int i = 0; i < viewTypeCount; ++i) {                    final ArrayList<View> scrapPile = scrapViews[i];                    views.addAll(scrapPile);                }            }        }

        将mScrapView中所有的缓存view全部添加到指定的view list中,只看到有AbsListView.reclaimViews有调用到,但没有其它方法使用这个函数,可能在特殊情况下会使用到,但目前从framework中,看不出来。

        3.2.13 setCacheColorHint

        /**         * Updates the cache color hint of all known views.         *         * @param color The new cache color hint.         */        void setCacheColorHint(int color) {            if (mViewTypeCount == 1) {                final ArrayList<View> scrap = mCurrentScrap;                final int scrapCount = scrap.size();                for (int i = 0; i < scrapCount; i++) {                    scrap.get(i).setDrawingCacheBackgroundColor(color);                }            } else {                final int typeCount = mViewTypeCount;                for (int i = 0; i < typeCount; i++) {                    final ArrayList<View> scrap = mScrapViews[i];                    final int scrapCount = scrap.size();                    for (int j = 0; j < scrapCount; j++) {                        scrap.get(j).setDrawingCacheBackgroundColor(color);                    }                }            }            // Just in case this is called during a layout pass            final View[] activeViews = mActiveViews;            final int count = activeViews.length;            for (int i = 0; i < count; ++i) {                final View victim = activeViews[i];                if (victim != null) {                    victim.setDrawingCacheBackgroundColor(color);                }            }        }

        该方法就是为所有的view绘置它们的背景色。

四、总结

        通过分析Adapter, BaseAdapter之间的关系,以及分析RecycleBin里面的方法,和对应使用的情况,我想,大家应该也很清楚了。RecycleBin是一个很重要的类,学习了这个类,无论是我们通过ListView 或 GridView来间接使用它,还是将来自己写控件时,需要考虑到复用view时,都很有帮助。

        ListView, GridView 以及 AbsListView 包括它们的parent都是很复杂的,我们不能一头埋进去,否则你会发现无从下手,我们只能通过一些关键的类,接口或方法,来找到突破口,理解了这些代码,再去看整个类时,你会发现,其实没那么神秘,甚至有些地方我们都可以联想到情景。

        好了,暂时就这么多,当然,对ListView的分析仍未结束,因为,我们只是了解了它的VIEW的添加,删除机制,但它如何与用户交互(即Touch,Scroll,Fling,Click, LongClick等)都未分析,大家可以尝试着去看看,我会在之后的时间里,慢慢的带领大家一起学习,分析。

       有什么不对的地方,或是值得大家分析,讨论的,也欢迎大家积极留言,谢谢!

  相关解决方案