2012-03-15 33 views
9

這裏還是一個相當新的Android開發人員。快速滾動時getView調用錯誤位置

我遇到了一個奇怪的問題,我不知道如何解決。我在這裏看到很多問題,聽起來像是我遇到的同樣的問題,但是他們的問題的解決方案似乎從來不適用於這裏發生的事情。

我有一個自定義的listView設置使用一個單獨的類,擴展BaseAdapter和使用視圖持有人(我設置了這看起來各種不同的自定義列表視圖的例子)。它有一個文本視圖,一個按鈕,一個進度條和一個圖像按鈕,這些項目根據用於下載文件的AsyncTask進行隱藏/更改。

當我慢慢滾動列表時,一切正常。當我快速向上/向下滾動列表時,就好像列表中某些單元格的視圖重新分配給其他單元格一樣。

我把這個線在我的「getView」法頂在我的適配器類:

@Override 
    public View getView(final int pos, View convertView, ViewGroup parent) 
    { 
     Log.i("ks3","getView called, position is " + pos); 

我在我的名單7個項目,其中6適合在屏幕上。當它第一次被顯示出來,我看到這個在我的日誌:

getView called, position is 0 
getView called, position is 1 
getView called, position is 2 
getView called, position is 3 
getView called, position is 4 
getView called, position is 5 

當我慢慢地向下滾動到下一個項目,這是打印出來下一個:

getView called, position is 6 

當我向後滾動起來,這:

getView called, position is 0 

往下走慢慢地產生這些結果完美無缺。

當我開始來回滾動很快,它打印出此消息一幫次,多出6和0的位置,但有時我會看到這些還有:

getView called, position is 1 
getView called, position is 5 

位置1和5不應該被調用,因爲它們總是在屏幕上。就好像getView弄得一團糟。同時,正如我之前所說的,我的列表看起來很奇怪。圖像按鈕將在不應該出現的單元格上移或下移。

如果我順利回去滾動,我再次只看到0和6的位置。

我真的不知道如何解決這個問題。我想也許我可以限制你能夠滾動列表的速度,但一直沒有找到任何可行的方法。

謝謝!

編輯: 我想更新關於這個問題的一些事情。第一個是,從這裏發表的評論以及來自listView上的Google視頻,我注意到getView可以被稱爲其他事物,然後是我想象的那樣,比如測量,所以我不應該感到驚慌通過我原本認爲是我的問題的一部分(那是因爲我認爲getView被錯誤的位置調用)。其次,我多次看到「緩存適配器內的視圖」是一個非常糟糕的主意。我不完全清楚這意味着什麼,但我敢肯定,這是我做錯的事情之一(我保存一個按鈕的實例,progressBar,imageButton ...)。

這,以及我更新getView外的視圖,並且我不使用notifyDataSetChanged();這些可能會導致一些不可思議的事情發生。

+0

除非你的自定義ListView做了一些奇怪的事情,這聽起來像是一個回收問題,發佈完整的getView()代碼。 – dmon 2012-03-15 01:02:22

+0

如果您在代碼中找不到任何錯誤,並且使用Honeycomb或更高版本,則可以嘗試爲列表佈局禁用硬件加速。硬件加速可能會導致此類錯誤。 – zapl 2012-03-15 16:37:21

+1

它感覺像是一個回收問題。我要看看listViews上的Google視頻,看看我使用listView時是否有任何明顯的問題。 – Droidmon 2012-03-15 16:50:06

回答

13

你是對的,ListView在屏幕上的不同位置重複使用視圖。這是一個優化,通過不分配新視圖來保持內存使用的合理和快速。您可能會錯誤地使用LiewView。觀看this talk on how to properly use ListView即可獲得全部內容,但以下是亮點:

  1. 「位置」是指數據在適配器列表中的位置。如果您的適配器數據在數組中,則這將成爲該數組的索引。
  2. 「ID」是指數據本身的值。如果你有一個名單並對他們進行評估,他們的位置將會改變,但他們的ID不會。
  3. 「索引」是指視圖相對於可視屏幕區域的相對位置。你可能永遠不需要這個。
  4. 請勿在適配器的getView()方法之外操縱視圖(或試圖緩存它們),否則您會收到奇怪的行爲。
  5. 如果對getView(int, View, ViewGroup)的調用提供視圖實例,請填充其字段,而不是膨脹全新的視圖。假設你已經正確實施了getItemType(),你總能得到正確的View類型來重新填充。
  6. 儘可能快地制定您的getView()方法,並且只能在其他線程上繁重工作。
  7. 僅因爲容器調用getView()並不一定意味着數據將被顯示。該框架將這些用於測量目的。由於工作可能會被拋棄,這是確保getView()的速度儘可能快的另一個原因。
  8. 當您需要在屏幕上顯示的數據發生問題時,請說明您的下載已完成,即您撥打notifyDataSetChanged()時。不要直接擺弄視圖,它們會在重繪時在下一個UI循環中填充。

剛剛花了幾天時間重做一個天真地執行的ListView,我感到你的痛苦。雖然結果是值得的!

+2

亞皆老街,非常感謝您的鏈接。我不清楚listView是如何工作的,我承認我正在更新getView之外的視圖,所以我必須弄清楚如何使用notifyDataSetChanged()。 – Droidmon 2012-03-15 16:47:04

+0

先生,請感受我的痛苦!你可以回答這個問題http://stackoverflow.com/questions/22451081/how-to-track-the-position-of-item-while-scrolling-in-a-long-listview – 2014-03-17 10:00:54

+0

我有一些中等重處理和添加每個項目的動態形狀,我只是找到getview()的地方。所以,當我可以移動它時,請問你的觀點? – Kenji 2016-03-06 08:48:21

0

getView對每個需要繪製的列表項都會調用,但以前在屏幕上不可見。如果你快速滾動,你可能會得到奇怪的位置,但不應該如你所描述的那樣導致錯誤(我猜Android在這裏沒有錯誤,因爲這些ListViews已經很好地測試過了)。例如。當你滾動得足夠快時,你可能會有2個需要繪製的視圖。 也許你的代碼有其他錯誤。

+0

嗯,好的,這是很好的知道,我很高興這不是,或者至少不應該是什麼導致問題。 – Droidmon 2012-03-15 16:48:07

4

總是在getview中使用這種方法: convertView = inflater.inflate(R.layout.listinflate,parent,false);

並讓您的活動實現onScrollListener,當用戶扔光標時通知您的列表視圖適配器,從不介意努力正確地更新項目。並且當用戶不再扔掉時,告訴適配器在getView中提供數據。

這解決了我所有的問題。 還有一件事情不要在列表視圖高度中使用wrap_content。在其中也設置了smoothscroll false。

+0

不使用wrap_content幫助我 – pkacprzak 2014-09-16 15:00:52

1

我有同樣的問題:進入適配器我改變了getView方法的背景顏色,但有時候listview是「reciclyng」視圖(獲取錯誤的背景)。 我解決乾脆把 「別人」 每個

if(...){changeBackground}

我加

else {restore default background}

然後它順利

+0

男人,你太棒了!我早上花了一大筆時間來研究這個問題。非常感謝! – 2014-11-20 16:34:34

+0

沒問題!很高興聽你這樣說! ;) – Mark 2014-11-25 16:22:41

0

工作了記錄,並延長@馬克

的答案

Android執行下一個任務。假設我有一個包含100個元素的列表。當您正在加載時,getview被稱爲元素0,1,2,3 ...至10。如果我們繼續,下一個位置將是11.但是,位置11回收的位置與位置0相同。因此,存在位置11的視圖,但存在錯誤的值(0值)。

答案:修改數據,如果視圖爲空或不是。如果視圖爲空,則放大並修改視圖的值。如果不是,則修改視圖的值。

比方說,我有一個item_view(佈局)與單一的TextView,則適配器應爲:

正確的版本:

public class XXXAdapter extends ArrayAdapter<XXX> {.... 
@NonNull 
@Override 
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 
    if (convertView==null) { 
     LayoutInflater inflater = LayoutInflater.from(this.getContext()); 
     convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false); 
    } 
    ***Object** item=this.getItem(position); 
    TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**); 
    txt.setText(String.valueOf(position)); 
    return convertView; 
    // return super.getView(position, convertView, parent); 
} 

版本錯誤:

public class XXXAdapter extends ArrayAdapter<XXX> {.... 
@NonNull 
@Override 
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 
    if (convertView==null) { 
     LayoutInflater inflater = LayoutInflater.from(this.getContext()); 
     convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false); 
     ***Object** item=this.getItem(position); 
     TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**); 
     txt.setText(String.valueOf(position)); 
     return convertView; 
     // return super.getView(position, convertView, parent); 
    } 
}