View the reuse mechanism of ListView from the source code of Android
Whether it is android or iOS, the list view should be the most complex control. The listview in android can be seen as a one-dimensional array, while the tableview in iOS is a two-dimensional array, but in fact, we need to pay attention to the similar aspects, all of which are reuse mechanisms. This is the best way to consider whether you can master listview.
The common code for initializing listview and setting the adapter is as follows:
ListView listView;MyAdapter listAdapter;ArrayList
listString;listView = (ListView)this.findViewById(R.id.listview);listString = new ArrayList
();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 stubmContext = context;inflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn listString.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn listString.get(arg0);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn 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);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn mData.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int arg0) {// TODO Auto-generated method stubreturn 0;}@Overridepublic 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() {@Overridepublic void onClick(View v) {showInfo();}});return convertView;}}
The setAdapter is mainly used to set data. Let's take a look at the setAdapter source code of ListView (source code here ).
@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(); }
The code above shows that it is divided into two steps. The first step is to clear the local adapter data and then set new data to listview if the current adapter is not empty. There is an important class in it.
AdapterDataSetObserver
Is used to store data. Let's take a look at the code, there is a member variable
mDataSetObserver
This object is declared in the parent class AbsListView of listview (source code is here)
So what is AdapterDataSetObserver? Let's go to the parent class AdapterView (source code here) of AbsListView to find out exactly.
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; } }
From the code above, we can see that this class actually inherits from DataSetObserver (source code here ). The observer mode is used for processing listview data.
This is an abstract class.
Not complete...