Android ListView的OnItemClickListener详解

<p ><span >我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener。本文主要在于对OnItemClickListener的<a ></a><strong >position</strong>和id参数做详细的解释,我相信有些人在这上面走了些弯路。</span></p><p ></p><p>先来看一下官方的文档</p><table><tbody ><tr class="firstRow"><th ><strong >position</strong></th><td >The<strong >position</strong>of the view in the adapter.</td></tr><tr ><th >id</th><td >The row id of the item that was clicked.</td></tr></tbody></table><p>而这两行字并没有解释清楚position和id的区别。另外,我们还有个Adapter的getView方法。</p><p>public abstract View getView (int position, View convertView, ViewGroup parent)</p><p>这里也有一个position。</p><p></p><p>初步接触ListView的同学,一般会直接继承ArrayAdapter,然后(比如我),就想当然的认为OnItemClick的position和getView的position是一样的啊。于是我们就getItem(position)来获取相应的数据。</p><p></p><p>那么这段代码有没有错呢?如果有错的话,在什么情况会出错呢?</p><p>第一个问题的答案是,当我们为ListView添加headerView或者footerView之后,这段代码就不一定是我们想要的了。</p><p></p><p>出现问题的原因在于,当我们为ListView添加headerView或者footerView之后,ListView在setAdapter时,做了一些事情,这导致,Adapter和OnItemClickListener中的position含义发生了变化。</p><p></p><p></p><p>我们可以来看看ListView中setAdapter的实现</p><pre class="brush:php;toolbar:false"> publicvoidsetAdapter(ListAdapteradapter){ if(mAdapter!=null&amp;&amp;mDataSetObserver!=null){ mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); if(mHeaderViewInfos.size()&gt;0||mFooterViewInfos.size()&gt;0){ mAdapter=newHeaderViewListAdapter(mHeaderViewInfos,mFooterViewInfos,adapter); }else{ mAdapter=adapter; }</pre><p>可以看出,如果这个ListView存在headerView或者footerView的话,那么会在我们传入的adapter外面在封装一层HeaderViewListAdapter,这是一个专门用来自动处理headerView和footerView的adapter。在ListView中,本身不区分headerView,footerView。ListView可以理解成是只负责管理一组View的数组的UI(ViewGroup),headerView和footerView都委托给HeaderViewListAdapter来处理。(从这里也可以看到为什么API文档中提到,addFooterView和addHeaderView要在setAdapter函数之前调用,如果在之后调用,那么就不会生成HeaderViewListAdapter,从而导致显示不出headerView和footerView)。</p><p></p><p>回到开头的问题,position和id有啥区别。为此,我们找一下position和id是怎么传进来的。</p><p>OnItemClickListener在android.widget.AdapterView的public boolean performItemClick(View view, int position, long id)函数中被调用。</p><p>performItemClick在android.widget.AbsListView.PerformClick.run() 中被调用</p><pre class="brush:php;toolbar:false">privateclassPerformClickextendsWindowRunnnableimplementsRunnable{ intmClickMotionPosition; publicvoidrun(){ //Thedatahaschangedsincewepostedthisactionintheeventqueue, //bailoutbeforebadthingshappen if(mDataChanged)return; finalListAdapteradapter=mAdapter; finalintmotionPosition=mClickMotionPosition; if(adapter!=null&amp;&amp;mItemCount&gt;0&amp;&amp; motionPosition!=INVALID_POSITION&amp;&amp; motionPosition&lt;adapter.getCount()&amp;&amp;sameWindow()){ finalViewview=getChildAt(motionPosition-mFirstPosition); //Ifthereisnoview,somethingbadhappened(theviewscrolledoffthe //screen,etc.)andweshouldcanceltheclick if(view!=null){ performItemClick(view,motionPosition,adapter.getItemId(motionPosition)); } } } }</pre><p>可以看到,position事实上就是ListView中被点击的view的位置。注意,在ListView中是不负责处理headerView和footViewer的,所以,这个位置应该是这个被点击的view在数组[所有的headerView,用户添加的view,所有的footerView]中的位置(请自行参考HeaderViewListAdapter的getView实现)。而id是来自于adapter.getItemId(position)。</p><p></p><p>对于ArrayAdapter的getItemId函数,实现就是return position。id和position是一致的。</p><p>然而,对于HeaderViewListAdapter</p><pre class="brush:php;toolbar:false"> publiclonggetItemId(intposition){ intnumHeaders=getHeadersCount(); if(mAdapter!=null&amp;&amp;position&gt;=numHeaders){ intadjPosition=position-numHeaders; intadapterCount=mAdapter.getCount(); if(adjPosition&lt;adapterCount){ returnmAdapter.getItemId(adjPosition); } } return-1; }</pre><p>实现逻辑是,如果position指向了headerView或footerView,那么返回-1,否则,将返回在用户view数组的位置。</p><p>也就是说</p><p>id=position-headerView的个数(id &lt; headerviewer的个数+用户view的个数),否则=-1</p><p>因此,OnItemClickListener的正确实现如下:</p><p></p><pre class="brush:php;toolbar:false">voidonItemClick(AdapterViewparent,Viewview,intposition,longid){ if(id==-1){ //点击的是headerView或者footerView return; } intrealPosition=(int)id; Titem=getItem(realPosition); //响应代码 }</pre><p></p>
RangeTime:0.003499s
RangeMem:203.3 KB
返回顶部 留言