我們在使用ListView的時候,一般都會為ListView添加一個響應事件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方法。
這里也有一個
position。
初步接觸ListView的同學,一般會直接繼承ArrayAdapter,然后(比如我),就想當然的認為OnItemClick的
position和getView的
position是一樣的啊。於是我們就getItem(
position)來獲取相應的數據。
那么這段代碼有沒有錯呢?如果有錯的話,在什么情況會出錯呢?
出現問題的原因在於,當我們為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
