notifyDataSetChanged()
当我们调用BaseAdapter的NotifyDataSetChanged()时,为什么ListView会全局刷新呢?
/**
* 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();
}
其中调用了DataSetObservable的notifyChanged():
public class DataSetObservable extends Observable<DataSetObserver> {
……
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
……
}
mObservers是DataSetObservable的成员变量,一个ArrayList,可以在DataSetObservable的父类Observable< T >中看出:
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
……
}
从以上代码可以看出notifyChanged()调用的是DataSetObserver的onChanged():
public void onChanged() {
// Do nothing
}
DataSetObserver是一个抽象类,onChanged()默认是空实现,那我们就试着去找它的具体子类。
猜想应该是在ListView的setAdapter()中设置了DataSetObserver的子类:
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(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();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
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();
}
可以看到new了一个AdapterDataSetObserver,并且作为参数传进了BaseAdapter的registerDataSetObserver():
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
其中调用了Observable的registerObserver():
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);
}
}
这里把刚才new的AdapterDataSetObserver添加到了观察者的集合中,当调用notifyDataSetChanged()时就会调用AdapterDataSetObserver的onChange():
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();
}
}
}
AdapterDataSetObserver 是ListView的一个内部类,它继承自AdapterView的一个也叫AdapterDataSetObserver 内部类:
@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();
}
在这里可以看到,notifyDataSetChanged()最终调用了requestLayout(),请求了View的重绘。
关于requestLayout(),可以看这篇文章View的绘制流程(Android开发艺术探索学习笔记)。
很多时候我们只要刷新ListView中的一条item即可,不用每次都notifyDataSetChanged(),因为每次对整个ListView进行重绘,很耗费性能,所以下面来介绍一下ListView的局部刷新。
其实很简单就是手动调用一下Adapter的getView(),但是还是要根据实际情况,倘若你需要刷新的这个item还没在界面上展示就没必要刷新了,因为等到它要出现在界面上的时候自然会调用getView()。下面看一下Google推荐的做法:
private void updateItem(int position) {
/**第一个可见的位置**/
int firstVisiblePosition = listView.getFirstVisiblePosition();
/**最后一个可见的位置**/
int lastVisiblePosition = listView.getLastVisiblePosition();
/**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/
if (position >= firstVisiblePosition && position <= lastVisiblePosition) {
/**获取指定位置view对象**/
View view = listView.getChildAt(position - firstVisiblePosition);
adapter.getView(position, view, listView);
}
}
参考:
1.Android源码分析之NotifyDataSetChanged()
2.Android ListView优化之局部刷新(更新)(非notifyDataSetChanged)