Android ListView的OnItemClickListener详解

我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener。本文主要在于对OnItemClickListener的position和id参数做详细的解释,我相信有些人在这上面走了些弯路。

 

先来看一下官方的文档
positionThe position of the view in the adapter.
idThe row id of the item that was clicked.
而这两行字并没有解释清楚 position和id的区别。另外,我们还有个Adapter的getView方法。
public abstract View getView (int positionView 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的实现

 

 public void setAdapter(ListAdapter adapter) {
      if (mAdapter != null && mDataSetObserver != null) {
          mAdapter.unregisterDataSetObserver(mDataSetObserver);
      }
      resetList();
      mRecycler.clear();
      if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
          mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
      } else {
          mAdapter = adapter;
      }
可以看出,如果这个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() 中被调用
  private class PerformClick extends WindowRunnnable implements Runnable {
      int mClickMotionPosition;
      public void run() {
          // The data has changed since we posted this action in the event queue,
          // bail out before bad things happen
          if (mDataChanged) return;
          final ListAdapter adapter = mAdapter;
          final int motionPosition = mClickMotionPosition;
          if (adapter != null && mItemCount > 0 &&
                  motionPosition != INVALID_POSITION &&
                  motionPosition < adapter.getCount() && sameWindow()) {
              final View view = getChildAt(motionPosition - mFirstPosition);
              // If there is no view, something bad happened (the view scrolled off the
              // screen, etc.) and we should cancel the click
              if (view != null) {
                  performItemClick(view, motionPosition, adapter.getItemId(motionPosition));
              }
          }
      }
  }
可以看到, position事实上就是ListView中被点击的view的位置。注意,在ListView中是不负责处理headerView和footViewer的,所以,这个位置应该是这个被点击的view在数组[所有的headerView,用户添加的view,所有的 footerView]中的位置(请自行参考HeaderViewListAdapter的getView实现)。而id是来自于adapter.getItemId( position)。
 
对于ArrayAdapter的getItemId函数,实现就是return  position。id和 position是一致的。
然而,对于HeaderViewListAdapter

 

public long getItemId(int position) {
      int numHeaders = getHeadersCount();
      if (mAdapter != null && position >= numHeaders) {
          int adjPosition = position - numHeaders;
          int adapterCount = mAdapter.getCount();
          if (adjPosition < adapterCount) {
              return mAdapter.getItemId(adjPosition);
          }
      }
      return -1;
  }
实现逻辑是,如果 position指向了headerView或 footerView,那么返回-1,否则,将返回在用户view数组的位置。
也就是说
id= position-headerView的个数(id < headerviewer的个数+用户view的个数),否则=-1
因此,OnItemClickListener的正确实现如下:

 

void onItemClick(AdapterViewparent, View view, int position, long id){
    if(id == -1) {
        // 点击的是headerView或者footerView
        return;
    }
    int realPosition=(int)id;
    T item=getItem(realPosition);
    // 响应代码
}

 

 

REFERENCES:http://blog.csdn.net/gg137608987/article/details/7995671

转载于:https://www.cnblogs.com/anee/archive/2013/01/30/2882986.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>