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中,理应是数据源的索引位置。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!