notifyDataSetChanged()源码分析以及如何实现ListView局部刷新
2017-05-27 10:40
691 查看
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 ext 4000 ends 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)
相关文章推荐
- Heritrix源码分析(九) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL
- BitSet数据结构以及jdk中实现源码分析
- Heritrix源码分析(十一) Heritrix中的URL--CandidateURI和CrawlURI以及如何增加自己的属性
- 如何使用androidpn实现android手机消息推送(简单的源码分析)
- Heritrix源码分析(九) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL
- .net MVC中如何使用iframe实现局部刷新
- android smack源码分析——接收消息以及如何解析消息
- android smack源码分析——接收消息以及如何解析消息
- lucene4.5源码分析系列:索引缓存以及刷新
- Struts2源码分析 初步1 --如何入手以及做了哪些初始化
- android smack源码分析——接收消息以及如何解析消息
- ecshop模板中如何实现局部刷新
- Heritrix源码分析(九) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL
- 如何用UpatePanel实现省市区的局部刷新
- 如何使用androidpn实现android手机消息推送(简单的源码分析)
- Heritrix1.14源码分析(9) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL
- Heritrix1.14源码分析(11) Heritrix中的URL--CandidateURI和CrawlURI以及如何增加自己的属性
- Android学习笔记---19_采用ListView实现数据列表显示,以及各种适配器使用,和如何写自己的适配器
- Heritrix源码分析(十一) Heritrix中的URL--CandidateURI和CrawlURI以及如何增加自己的属性(转)
- Java如何实现多态性,基于itable, vtable源码分析