ListView作为常用的列表控件,通常会定义不同适配器以承载其所绑定数据的特殊显示。常用的适配器(ArrayAdapter,
BaseAdapter等)也都用过,但是大家有没有考虑过为什么要用适配器?
问题一:适配器是承载数据的,如何跟listview建立联系?
疑问我们需要了解适配器Adapter基础接口:registerDataSetObserver (注册数据监听),unregisterDataSetObserver (取消数据监听) 。这种设计意图很明显,数据发生变化的时候同步通知到观察者本身。而观察者是谁?我们调用adapter的时候并没有去注册任何监听,唯一跟listview交互的入口也就setAdapter()。
查阅setAdapter方法源码可知:
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mAdapter.registerDataSetObserver(mDataSetObserver);
....
}
的确,listview内部注册了适配器监听(这个时候适配器也就和listview建立关联)。
问题二: 适配器何时需要通知ListView去变化?
我们先看Adpater的实现类BaseAdapter,大家通常都会用到 notifyDataSetChanged(), notifyDataSetInvalidated(),然后ListView就发生了变化。
BaseAdapter部分源码:
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
mDataSetObservable.unregisterObserver(observer);
}
public void
notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
mDataSetObservable.notifyChanged();
}
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
可以发现最终调用的是listview所注册的观察者中(onInvalidate, onChange)
问题三:适配器数据发生变化,listview本身发生了什么变化?
我们看最终dataobserver中做了什么 (AdapterDataSetObserver为adapterview的内部类):
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
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();
}
// 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;
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();
}
// 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;
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
requestLayout();
}
最终知道了,listview本身调用requestlayout实现全局刷新。