ListView源码解析(二) Adapter
1 说明
在开始解析源码之前,至少要了解观察者模式,不懂的可以去百度一下,稍微了解一下就好.
其实 Adapter相当于是主题类,ListView相当于是订阅者,先明白这个,可能就容易理解很多
2 源码分析
首先咱们从 setAdapter方法开始分析
- ListView
/**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
* @param adapter The ListAdapter which is responsible for maintaining the
* data backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
@Override
public void setAdapter(ListAdapter adapter) {
//注释1
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//清空ListView
resetList();
//清空View缓存
mRecycler.clear();
//处理头尾布局
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
//注释2
//注册订阅者
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
//给RecycleBin设置 ViewType
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
//重新布局
requestLayout();
}
从上面可以看出,设置adapter,其实就是给mAdapter赋值,然后调用requestLayout()进行重新布局,上篇我们讲了ListView的绘制流程,那么这里就不多说
从注释1 我们可以看到,如果有adapter,那么就将mDataSetObserver移出注册,在注释2处,又重新注册了一个AdapterDataSetObserver类型的对象,那么这个对象是什么呢,我们跟进去看
它其实是AbsListView的一个内部类
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
//快速滑动
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
//快速滑动
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
}
里面有两个方法,一个changed,一个重绘,其实调用的都是super的方法,我们跟进
这个类又是AdapterView的内部类
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
上面的两个方法,onInvalidated/onChanged最后都调用了 requestLayout()方法,
那这两个方法有什么区别呢,他还有个父类,我们继续跟进,主要是看注释
/**
* Receives call backs when a data set has been changed, or made invalid. The typically data sets
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
* 当数据变化或无效的时候会被调用,标准的一个订阅者,最后又说必须添加到DataSetObservable,这个就是主题类,这里就看可以看出,这是一个观察者
*/
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
* 当数据变化的时候调用
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
* 数据全部无效的时候调用
*/
public void onInvalidated() {
// Do nothing
}
}
从上面,我们可以大胆的猜测,adapter中维护了一个订阅者队列,当调用notifyDateSetChange的时候,会回调订阅者里面的方法.
我们再回到 setAdater方法中,进入adapter.registerDataSetObserver方法,看看是不是正如我们所想的那样,我们先看一个类图
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;
}
}
如果了解观察者模式的话,到这里基本逻辑就已近很清晰了,注册移除和notifyDateSetchanged都是通过DataSetObservable完成的
我们看一下这个对象和他的父类
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
public abstract class Observable<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
我删除了一些注释,这里,其实注册订阅者,就是将他们放到一个集合中,当我们需要notify的时候,逐个调用他们的方法(onChanged/onInvalidated),移除注册就是从列表中移除注册的对象
到这里,我们的分析就结束了,下面总结一下
3 总结
Adapter内部维护了一个DataSetObservable对象,这是一个主题类,内部有一些订阅者,在给ListView.setAdapter的时候,会注册一个订阅者进去,我们调用adapter.notifyDataSetChanged的时候,会调用DataSetObservable.notifyChanged()方法,这个方法会逐个遍历DataSetObservable内部的订阅者对象,然后调用他们的onChanged方法,在onChanged方法调用 requestLayout();进行ListView的数据重新填充.
在这里主题类由BaseAdapter.mDataSetObservable担当,订阅者由AbsListView的内部类AdapterDataSetObserver担当