关于listView,我想说的

导读

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是在这里执行的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值