导读
listView在android中是很重要的控件,也是菜鸟view入门的一个很好的台阶。通过它可以了解view的滑动,观察者模式,缓存,生产者消费者模式,以及listView特有的view复用机制等等。
个人比较遗憾的是,接触listView已经很久了,但一直没有真的理解(可能之前理解,但现在忘记了)。
通过最近这个项目,重新接触了listView,在项目中也遇到了一些问题,犯了一些错,相信这些问题是我们在使用listView是过程中都会遇到的,为了防止再犯,加深记忆,特记录在此。
项目的需求是做一个单选的listView
主要会从这几个方面进行展开
- adapter适配器的绑定
- setAdapter和adapter.notifyDataChanged的区别
- listView的复用机制 RecycleBin,结合源码分析
- 利用 缓存进行优化 使活动更加流畅,但也可能因为缓存引起的一些问题
- 下拉刷新、分页加载的实现
1,listView绑定适配器
ListView listView = findViewById(R.id.list);
FruitAadapter<Fruit> adapter =
new FruitAadapter<Fruit>(this);
adapter.setData(list);//配置数据
listView.setAdapter(adapter);//listView绑定adapter
adapter最重要的一个方法就是
adapter.notifyDataSetChanged();
故名思意就是,数据改变的时候view改变。实际结果就是刷新listView。
常用操作就是:
adapter.setData(list);
adapter.notifyDataSetChanged();//两者配合一起使用
那问题来个,第一次绑定adapter的时候为什么没用调用notifyDataSetChanged方法,那个时候是怎么实现数据绑定的呢。
setAdapter和notifyDataSetChanged 两者有什么区别呢。
2,setAdapter和adapter.notifyDataChanged的区别
这个我只粗略介绍一下,想要详细了解的可以看其他大牛的源码详解
listView.setAdapter:
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
//解绑之前的观察者 mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;//为mAdapter赋值
}
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());//Recycler很重要的一个类
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();//请求刷新页面
}
AdapterDataSetObserver是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;
}
}
adapter.notifyDataSetChanged 调用 BaseAdapter的 mDataSetObservable.notifyChanged():
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
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();//调用在setAdapter方法中实例化的DataSetObserver。
}
}
}
/**
* Invokes {@link DataSetObserver#onInvalidated} on each observer.
* Called when the data set is no longer valid and cannot be queried again,
* such as when the data set has been closed.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
由此 可以看出listView绑定adapter的思路是,在setAdapter的时候,在mDataSetObservable注册了观察者DataSetObserver(AdapterView的内部类),notifyDataSetChanged 时,调用观察者的onChange。达到刷新的目的。
setAdapter和notifyDataSetChanged区别就是
- setAdapter要先unregister之前的adapter,重新注register新的adapter
- notifyDataSetChanged是直接刷
setAdapter和onChange中最后都调用了requestLayout。所以真正的刷新View是在这里执行的。