android.widget.AdapterView.OnItemClickListener。本文主要在于对OnItemClickListener的position和id参数做详细的解释,我相信有些人在这上面走了些弯路。
先来看一下官方的文档
position The position of the view in the adapter. id The row id of the item that was clicked.
而这两行字并没有解释清楚position和id的区别。另外,我们还有个Adapter的getView方法。
public abstract View getView (int
position, View convertView, ViewGroup parent)
这里也有一个position。
初步接触ListView的同学,一般会直接继承ArrayAdapter,然后(比如我),就想当然的认为OnItemClick的position和getView的position是一样的啊。于是我们就getItem(position)来获取相应的数据。
那么这段代码有没有错呢?如果有错的话,在什么情况会出错呢?
第一个问题的答案是,当我们为ListView添加headerView或者footerView之后,这段代码就不一定是我们想要的了。
出现问题的原因在于,当我们为ListView添加headerView或者footerView之后,ListView在setAdapter时,做了一些事情,这导致,Adapter和OnItemClickListener中的position含义发生了变化。
我们可以来看看ListView中setAdapter的实现 437publicvoid setAdapter(ListAdapter adapter) { 438if (mAdapter != null&&mDataSetObserver != null) { 439mAdapter.unregisterDataSetObserver(mDataSetObserver); 440 } 442resetList(); 443mRecycler.clear();
445if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
446mAdapter = newHeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
447 } else { 448mAdapter = adapter; 449 }
可以看出,如果这个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)。
回到开头的问题,position和id有啥区别。为此,我们找一下position和id是怎么传进来的。 OnItemClickListener在android.widget.AdapterView的public boolean performItemClick(View view, int position, long id)函数中被调用。
performItemClick在android.widget.AbsListView.PerformClick.run() 中被调用 2497privateclass PerformClick extendsWindowRunnnableimplementsRunnable { 2498int mClickMotionPosition; 2500publicvoid run() {
2501 // The data has changed since we posted this action in the event queue,
2502 // bail out before bad things happen 2503if (mDataChanged) return;
2505finalListAdapter adapter = mAdapter;
2506finalint motionPosition = mClickMotionPosition; 2507if (adapter != null&&mItemCount> 0 &&
2508 motionPosition != INVALID_POSITION&&
2509 motionPosition < adapter.getCount() &&sameWindow()) { 2510finalView view = getChildAt(motionPosition - mFirstPosition);
2511 // If there is no view, something bad happened (the view scrolled off the
2512 // screen, etc.) and we should cancel the click 2513if (view != null) {
2514performItemClick(view, motionPosition, adapter.getItemId(motionPosition)); 2515 } 2516 } 2517 } 2518 }
可以看到,position事实上就是ListView中被点击的view的位置。注意,在ListView中是不负责处理headerView和footViewer的,所以,这个位置应该是这个被点击的view在数组[所有的headerView,用户添加的view,所有的footerView]中的位置(请自行参考
HeaderViewListAdapter的getView实现)。而id是来自于adapter.getItemId(position)。
对于ArrayAdapter的getItemId函数,实现就是return position。id和position是一致的。 然而,对于HeaderViewListAdapter
188publiclong getItemId(int position) { 189int numHeaders = getHeadersCount();
190if (mAdapter != null&& position >= numHeaders) { 191int adjPosition = position - numHeaders; 192int adapterCount = mAdapter.getCount(); 193if (adjPosition < adapterCount) {
194returnmAdapter.getItemId(adjPosition); 195 } 196 } 197return -1; 198 }
实现逻辑是,如果position指向了headerView或footerView,那么返回-1,否则,将返回在用户view数组的位置。 也就是说
id=position-headerView的个数(id < headerviewer的个数+用户view的个数),否则=-1 因此,OnItemClickListener的正确实现如下:
view source print?
void onItemClick(AdapterViewparent, View 1
view, int position, long id){ 2 if(id<0) {
3 // 点击的是headerView或者footerView 4 return; 5 }
6 int realPosition=(int)id; 7 T item=getItem(realPosition); 8 // 响应代码 9 }
因篇幅问题不能全部显示,请点此查看更多更全内容