2011-07-08 54 views
6

我在使用StateListDrawable作爲背景時有一些非常奇怪的ListView行爲。我試圖按照this後的回答,因爲我沒有得到state_checked狀態,但現在我的ListView變得瘋狂了。ListView項目和狀態選擇器背景的奇怪行爲可繪製

當我點擊一個項目時,它不會立即改變顏色到選擇器中的state_checked項目。儘管點擊了一下,許多視圖會突然切換到state_checked背景。這看起來是隨機的。

這裏是我的狀態選擇XML代碼:

<?xml version="1.0" encoding="utf-8"?> 
<selector 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <item android:state_pressed="true" > 
     <shape> 
      <gradient 
       android:startColor="@color/grey" 
       android:endColor="@color/darkgrey" 
       android:angle="270" /> 
      <stroke 
       android:width="0dp" 
       android:color="@color/grey05" /> 
      <corners 
       android:radius="0dp" /> 
      <padding 
       android:left="10sp" 
       android:top="10sp" 
       android:right="10sp" 
       android:bottom="10sp" /> 
     </shape> 
    </item> 

    <item android:state_focused="true" > 
     <shape> 
      <gradient 
       android:endColor="@color/orange4" 
       android:startColor="@color/orange5" 
       android:angle="270" /> 
      <stroke 
       android:width="0dp" 
       android:color="@color/grey05" /> 
      <corners 
       android:radius="0dp" /> 
      <padding 
       android:left="10sp" 
       android:top="10sp" 
       android:right="10sp" 
       android:bottom="10sp" /> 
     </shape> 
    </item> 

    <item android:state_checked="true"> 
     <shape> 
      <gradient 
       android:endColor="@color/brown2" 
       android:startColor="@color/brown1" 
       android:angle="270" /> 
      <stroke 
       android:width="0dp" 
       android:color="@color/grey05" /> 
      <corners 
       android:radius="0dp" /> 
      <padding 
       android:left="10sp" 
       android:top="10sp" 
       android:right="10sp" 
       android:bottom="10sp" /> 
     </shape> 
    </item> 

    <item android:state_selected="true"> 
     <shape> 
      <gradient 
       android:endColor="@color/brown2" 
       android:startColor="@color/brown1" 
       android:angle="270" /> 
      <stroke 
       android:width="0dp" 
       android:color="@color/grey05" /> 
      <corners 
       android:radius="0dp" /> 
      <padding 
       android:left="10sp" 
       android:top="10sp" 
       android:right="10sp" 
       android:bottom="10sp" /> 
     </shape> 
    </item> 

    <item>   
     <shape> 
      <gradient 
       android:startColor="@color/white" 
       android:endColor="@color/white2" 
       android:angle="270" /> 
      <stroke 
       android:width="0dp" 
       android:color="@color/grey05" /> 
      <corners 
       android:radius="0dp" /> 
      <padding 
       android:left="10sp" 
       android:top="10sp" 
       android:right="10sp" 
       android:bottom="10sp" /> 
     </shape> 
    </item> 

</selector> 

這裏是我爲我的自定義視圖的.java類實現可檢驗:

public class Entry extends LinearLayout implements Checkable { 

    public Entry(Context context) { 
     super(context, null); 

     // Inflate this view 
     LayoutInflater temp = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     temp.inflate(R.layout.entry, this, true); 

     initViews(); 
    } 

    private static final int[] CheckedStateSet = { 
     android.R.attr.state_checked 
    }; 

    private void initViews() { 
     this.setBackgroundResource(R.drawable.listview_row); 
    } 

    public boolean isChecked() { 
     return _checked; 
    } 

    public void toggle() { 
     _checked = !_checked; 
    } 

    public void setChecked(boolean checked) { 
     _checked = checked; 
    } 

    @Override 
    protected int[] onCreateDrawableState(int extraSpace) { 
     final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 
     if (isChecked()) { 
      mergeDrawableStates(drawableState, CheckedStateSet); 
     } 
     return drawableState; 
    } 

    @Override 
    public boolean performClick() { 
     toggle(); 
     return super.performClick(); 
    } 
} 

我周圍戳了幾個小時嘗試弄清楚,但不幸的是必須承認尋求幫助。任何人都可以看到上面的代碼會導致ListView在項目上出現奇怪行爲的錯誤?如果需要,我也可以發佈更多代碼。

+0

我認爲你應該使用查看包裝類 – Nitin

回答

21

ListView工作是非常重要的,始終牢記的觀點是呈現和適配器是數據模型

這意味着所有的狀態應該在適配器(數據模型)中,而不是的視圖中。

從我可以告訴你的代碼,您有示出校驗狀態的觀點,並認爲國家是不是在適配器視圖。也就是說,當用戶點擊列表中的項目時,用於顯示其項目的視圖將其內部檢查狀態更改爲切換顯示給用戶的內容。

但是由於視圖不是數據模型,因此您在此處播放的狀態是暫時性的,並且實際上並未與正在單擊的適配器項關聯。

這個問題導致的最明顯的問題是視圖回收。當您滾動瀏覽ListView時,當項目滾動結束並且新項目出現在底部時,用於顯示舊項目的視圖將重新用於顯示新項目。這比每次顯示新項目時不得不膨脹新的項目視圖層次結構效率更高。

因爲你在視圖中擁有你的狀態,所以當這種回收發生時,視圖中的狀態會隨機地與某個新項目重新關聯。這可能發生在許多情況下,而不僅僅是滾動。

解決方案是將您的檢查狀態放在適配器中,並根據您現在在適配器中的狀態實施Adapter.getView()來設置視圖的已檢查狀態。這樣,無論何時回收視圖(並調用getView()以將新數據行綁定到該數據行),您將更新其檢查狀態以正確地遵循它顯示的新數據。

+0

ohhhh是的,我現在看到。所以現在,我的適配器從數據庫中檢索它的數據(它沒有「單擊」列,我認爲它也不應該)。最佳做法是將當前檢查的項目的狀態保留在適配器中的某種集合中?另外,當我使用'ListView.setItemChecked()'方法時,它是否也檢查適配器中的項目? –

+0

不要考慮使用ListView的檢查狀態,如果它做你想做的 - 它可以做單選或多選。它會照顧你需要做的事情 - 跟蹤與每個行ID相關聯的狀態(不是行索引,因爲如果數據庫中的數據發生變化,這些可以改變)。您需要讓UI正確地與列表視圖進行交互,以便通過在列表項的頂層視圖中實現Checkable來顯示此狀態。 ApiDemos中有這樣的例子。 – hackbod

+0

感謝hackbod,但我認爲這實際上讓我更加困惑,主要是因爲那正是我所做的。我的ListView頂級列表項目視圖是我爲上面發佈代碼的可檢驗項目類。我避免重寫過去的performclick()方法,並且依賴ListView的實例上的ListView.setItemChecked()方法。然而,這並不起作用,並且視圖不會將它們的背景更改爲我的state_checked項目的背景(即使其他狀態正確顯示)。這是一個單獨的問題,還是相關的? –

3

我不認爲問題來自上面的代碼。之前我遇到過這個問題,它只能在列表視圖中回收視圖。如果您的列表在屏幕上繼續顯示,則可能會出現這種情況。如果是這種情況,修復它的一個好方法是將項目的狀態存儲在一個列表中,以便您可以跟蹤它們並將它們的狀態建立在您創建的列表之外。請參閱thisthis瞭解有關回收視圖的更多信息。

0

當對一組相當複雜的ListView做類似工作時,我向適配器添加了一個額外的列表,並在其中添加了單擊項目的位置。 getView(...)然後膨脹/回收視圖,並在它完成之前檢查項目的狀態以及內部適配器狀態以決定應用哪個背景。

我還設置了狀態列表xml文件,以使背景在當前按下時顯示爲透明,因此選擇器可見,它可以工作。