您的位置:首页 > 移动开发 > Android开发

Android ListView的了解Adapter, BaseAdapter, RecycleBin

2014-09-04 11:27 417 查看
一、前述

通常,我们在使用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 {@link Adapter} that can be
* used in both {@link ListView} (by implementing the specialized
* {@link ListAdapter} interface} and {@link Spinner} (by implementing the
* specialized {@link 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 {@link #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
* {@link 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 {@link #getViewTypeCount()} and
*        {@link #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 {@link 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
* {@link #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 {@link #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 {@link #getView}. Note: Integers must be in the
*         range 0 to {@link #getViewTypeCount} - 1. {@link #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
* {@link #getView}. Each type represents a set of views that can be
* converted in {@link #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 {@link 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 {@link 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 {@link 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 {@link 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等)都未分析,大家可以尝试着去看看,我会在之后的时间里,慢慢的带领大家一起学习,分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: