今天在作項目的時候,遇到一個問題,記錄下來。html
當給ListView加了一個HeaderView後(代碼以下),咱們發現,onItemClick方法裏的position
參數的值不是咱們所指望的,好比點擊ListView的第一行,咱們指望的position
是0,但是實際上倒是1,也就是說,它是從Header而不是從第一行開始計數的。java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.home); mAdapter = new MyAdapter(this); mListView = (ListView) findViewById(R.id.list); mListView.addHeaderView(getLayoutInflater().inflate(R.layout.list_header)); mListView.setAdapter(mAdapter); mListView.setOnClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { doSomething(mAdapter.getItem(position)); }
Google了下,發現有個老外issue過一個bug,和我遇到的問題同樣,不過這個bug被RomainGuy reject掉了,理由是,你用錯了,請用getAdapter。這回答的太簡潔了,徹底無法理解,因此只好又去仔細研究ListView的代碼,終於領會他的意思了。把其中addHeaderView和setAdapter方法貼下來android
/** * Add a fixed view to appear at the top of the list. If addHeaderView is * called more than once, the views will appear in the order they were * added. Views added using this call can take focus if they want. * <p> * NOTE: Call this before calling setAdapter. This is so ListView can wrap * the supplied cursor with one that that will also account for header * views. * * @param v The view to add. * @param data Data to associate with this view * @param isSelectable whether the item is selectable */ public void addHeaderView(View v, Object data, boolean isSelectable) { if (mAdapter != null) { throw new IllegalStateException( "Cannot add header view to list -- setAdapter has already been called."); } FixedViewInfo info = new FixedViewInfo(); info.view = v; info.data = data; info.isSelectable = isSelectable; mHeaderViewInfos.add(info); } /** * Sets the data behind this ListView. * * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter}, * depending on the ListView features currently in use. For instance, adding * headers and/or footers will cause the adapter to be wrapped. * * @param adapter The ListAdapter which is responsible for maintaining the * data backing this list and for producing a view to represent an * item in that data set. * * @see #getAdapter() */ @Override public void setAdapter(ListAdapter adapter) { if (null != mAdapter) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } //其它的一些代碼這裏省略之... }
從代碼和註釋裏均可以很清楚的得知,addHeaderView
必定要在setAdapter
以前調用,若是不這樣作,addHeaderView
會拋出一個異常。Android爲何要這樣?這是由於,在setAdapter
的時候,會針對我遇到的這種狀況(也就是添加Header後position
不正確的這種狀況)作些特殊的處理。setAdapter
在內部判斷了當前ListView是否有Header或者Footer,若是沒有,就直接使用參數傳進來的adapter;若是有,則用一個decorated的HeaderViewListAdapter
來替換參數。這個HeaderViewListAdapter
的使命,就是排除Header和Footer,讓position
(固然也包括getItem, getItemId)等方法的position
參數)正確返回。app
分析到這裏,解決方案就出來了:在onItemClick
不要直接使用咱們聲明的adapter,而是用ListView裏的那個decorated adapter。獲取它的方法就是調用parent.getAdapter()
。固然,若是ListView沒有Header和Footer,直接使用聲明的adapter也沒有問題,不過爲了不出錯,仍是統一使用decorated adapter比較好。ide
把onItemClick改爲下面這樣,就能夠了this
@Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { doSomething(parent.getAdapter().getItem(position)); }
本文由Roy最初發表於:http://blog.chengbo.net/2012/03/09/onitemclick-return-wrong-position-when-listview-has-headerview.html,你能夠在保持文章完整和保留本聲明的狀況下轉帖、分發和印刷等。google