Android从源码看ListView的重用机制

笛里谁知壮士心,沙头空照征人骨。这篇文章主要讲述Android从源码看ListView的重用机制相关的知识,希望能为你提供帮助。
不管是android还是ios,列表视图应该是最复杂的控件了。android中的listview从命名能够看出是个一维数组,而iOS中的tableview则是二维数组。但事实上须要注意的地方是差点儿相同的。都是重用机制。这是考量你对listview是否能掌握的最好的方法。
常见的listview的初始化以及设置适配器的代码例如以下:

ListView listView; MyAdapter listAdapter; ArrayList< String> listString; listView = (ListView)this.findViewById(R.id.listview); listString = new ArrayList< String> (); for(int i = 0 ; i < 100 ; i++) { listString.add(Integer.toString(i)); } listAdapter = new MyAdapter(this); listView.setAdapter(listAdapter); }class MyAdapter extends BaseAdapter{Context mContext; LinearLayout linearLayout = null; LayoutInflater inflater; public MyAdapter(Context context) { // TODO Auto-generated constructor stub mContext = context; inflater = LayoutInflater.from(mContext); }@Override public int getCount() { // TODO Auto-generated method stub return listString.size(); }@Override public Object getItem(int arg0) { // TODO Auto-generated method stub return listString.get(arg0); }@Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } public final class ViewHolder{ public ImageView img; public TextView title; public TextView info; public Button viewBtn; } public class MyAdapter extends BaseAdapter{private LayoutInflater mInflater; public MyAdapter(Context context){ this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { // TODO Auto-generated method stub return mData.size(); }@Override public Object getItem(int arg0) { // TODO Auto-generated method stub return null; }@Override public long getItemId(int arg0) { // TODO Auto-generated method stub return 0; }@Override public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null; if (convertView == null) {holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.vlist2, null); holder.img = (ImageView)convertView.findViewById(R.id.img); holder.title = (TextView)convertView.findViewById(R.id.title); holder.info = (TextView)convertView.findViewById(R.id.info); holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn); convertView.setTag(holder); }else {holder = (ViewHolder)convertView.getTag(); }holder.img.setBackgroundResource((Integer)mData.get(position).get(" img" )); holder.title.setText((String)mData.get(position).get(" title" )); holder.info.setText((String)mData.get(position).get(" info" )); holder.viewBtn.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) { showInfo(); } }); return convertView; } }


当中setAdapter是主要用来设置数据的。我们不防看一下ListView(源代码在此)的setAdapter源代码

@Override public void setAdapter(ListAdapter adapter) { if (null != mAdapter) { 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; 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(); }if (mCheckStates != null) { mCheckStates.clear(); }requestLayout(); }


从以上代码能够看出其分为两步,第一步是当前adapter不为空的话。先清空本地adapter数据。然后是设置新的数据到listview。
当中里面有个重要的类
AdapterDataSetObserver


是用来存储数据的,我们看一下里面的代码,有个成员变量
mDataSetObserver



< span style=" font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255); " > 就是这个对象申明在listview的父类AbsListView(< a target=_blank href=https://www.songbingjia.com/android/" http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/widget/AbsListView.java/?v=source" > 源代码在此< /a> )中< /span>


那么这个AdapterDataSetObserver到底是个什么东西呢,我们还是到AbsListView的父类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; checkSelectionChanged(); checkFocus(); requestLayout(); }public void clearSavedState() { mInstanceState = null; } }


从以上代码能够看出,这个类事实上是继承自DataSetObserver(源代码在此)。
使用的是观察者模式,用于listview的数据处理。
这是一个抽象类,不多说了。
未完待续。
。。
【Android从源码看ListView的重用机制】





    推荐阅读