Android ListView position详解及实例代码
我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener。对OnItemClickListener的position和id参数,我相信有些人在这上面走了些弯路。
在使用listview的时候,我们经常会在listview的监听事件中,例如OnItemClickListener(onItemClick)中,或listview的adapter中(getView、getItem、getItemId等)看到position这个变量。在我们没有为listview添加headerView时,position和数据源集合的索引是一致的,当添加了headerView之后,某些地方的position值就会发生变化,如果不理解清楚,经常会犯一些糊涂。
在listview添加了headerView后,会将所有view交给HeaderViewListAdapter来处理,所以我们要在setAdapter之前添加headerView或footerView,否则将显示不出来。
@Override
publicvoidsetAdapter(ListAdapteradapter){
if(mAdapter!=null&&mDataSetObserver!=null){
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if(mHeaderViewInfos.size()>0||mFooterViewInfos.size()>0){
mAdapter=newHeaderViewListAdapter(mHeaderViewInfos,mFooterViewInfos,adapter);
}else{
mAdapter=adapter;
}
先看看HeaderListAdapter中几个带position参数的方法实现,我们可以看到在传出的position为adjPosition,而adjPosition均为我们自动去掉了headerView的数量,所以adapter中几个带position变量的方法,得到的position值均和数据源集合索引一致,仔细翻看HeaderListAdapter中所有需要传出position的方法,position的值都是自动减去了headerView数量。
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
//Header(negativepositionswillthrowanArrayIndexOutOfBoundsException)
intnumHeaders=getHeadersCount();
if(position<numHeaders){
returnmHeaderViewInfos.get(position).view;
}
//Adapter
finalintadjPosition=position-numHeaders;
intadapterCount=0;
if(mAdapter!=null){
adapterCount=mAdapter.getCount();
if(adjPosition<adapterCount){
returnmAdapter.getView(adjPosition,convertView,parent);
}
}
//Footer(off-limitspositionswillthrowanArrayIndexOutOfBoundsException)
returnmFooterViewInfos.get(adjPosition-adapterCount).view;
}
publicObjectgetItem(intposition){
//Header(negativepositionswillthrowanArrayIndexOutOfBoundsException)
intnumHeaders=getHeadersCount();
if(position<numHeaders){
returnmHeaderViewInfos.get(position).data;
}
//Adapter
finalintadjPosition=position-numHeaders;
intadapterCount=0;
if(mAdapter!=null){
adapterCount=mAdapter.getCount();
if(adjPosition<adapterCount){
returnmAdapter.getItem(adjPosition);
}
}
//Footer(off-limitspositionswillthrowanArrayIndexOutOfBoundsException)
returnmFooterViewInfos.get(adjPosition-adapterCount).data;
}
publiclonggetItemId(intposition){
intnumHeaders=getHeadersCount();
if(mAdapter!=null&&position>=numHeaders){
intadjPosition=position-numHeaders;
intadapterCount=mAdapter.getCount();
if(adjPosition<adapterCount){
returnmAdapter.getItemId(adjPosition);
}
}
return-1;
}
我们再来分析分析OnItemClickListener的相关源码,OnItemClickListener在android.widget.AdapterView的publicbooleanperformItemClick(Viewview,intposition,longid)函数中被调用。而performItemClick是在android.widget.AbsListView.PerformClick.run()中被调用:
privateclassPerformClickextendsWindowRunnnableimplementsRunnable{
intmClickMotionPosition;
publicvoidrun(){
//Thedatahaschangedsincewepostedthisactionintheeventqueue,
//bailoutbeforebadthingshappen
if(mDataChanged)return;
finalListAdapteradapter=mAdapter;
finalintmotionPosition=mClickMotionPosition;
if(adapter!=null&&mItemCount>0&&
motionPosition!=INVALID_POSITION&&
motionPosition<adapter.getCount()&&sameWindow()){
finalViewview=getChildAt(motionPosition-mFirstPosition);
//Ifthereisnoview,somethingbadhappened(theviewscrolledoffthe
//screen,etc.)andweshouldcanceltheclick
if(view!=null){
performItemClick(view,motionPosition,adapter.getItemId(motionPosition));
}
}
}
}
从源码中,我们可以看到position对应motionPosition,而motionPosition通过调试,我们发现就是listview中被点击的位置,所以我们经常在onItemClick中需要获取数据源集合中某个item时,会习惯性写这样代码:sourceList.get(position-listView.getHeaderViewsCount())。
我们发现onItemClick还有一个参数,其实就是上面源码中传递给performItemClick的第三个参数,而第三个参数是通过调用adapter的getItemId将motionPosition减去了headerView的数量,所以这个参数的结果是与数据源集合的索引一致的。也就是说,我们完全可以使用onItemClick的id这个参数,这个参数是和数据源集合的索引一致的。
另外我们需要注意,如果数据源没有内容,则id的值会为-1,所以我们在使用id时,需要对id做适当判断。
总结:在OnItemClickListener的onItemClick方法中,当我们需要获取点击listview对应的数据源索引时,使用id参数即可。另外除了onItemClick的position参数是点击listview对应view的位置外,adapter中所有position均为数据源索引位置。其实换个角度更容易记,在listview中,position理应是listview中view对应的位置,而在adapter中,理应是数据源的索引位置。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!