2016-06-11 134 views
0

我有一個使用自定義ArrayAdapter的列表視圖。 ListView的項目是RelativeLayouts。存儲在「Track」對象的「lightsOnThisTrack」列表中的「Light」視圖隨後被添加到其相應的RelativeLayouts中。在ListView中添加視圖到RelativeLayout產生重複的項目

問題是,如果我向ListView添加更多項目,以前添加到relativeLayouts的視圖開始在新添加的項目上重複。另一方面,TextView「trackText」沒有被重複,正如在示例中可以看到的那樣。正如我在其他帖子上看到的,我知道這是與ViewHolder模式實現方式有關的問題,但我無法找到問題所在。

Example of the ListView

public class TrackListAdapter extends ArrayAdapter<Track> { 

    private static final String TAG = "TrackListAdapter"; 
    private LayoutInflater layoutInflater; 
    public ArrayList<Track> trackArrayList; 
    Context mContext; 
    RelativeLayout relativeLayout; 

    public TrackListAdapter(Context context, ArrayList<Track> trackArrayList) { 
     super(context, 0, trackArrayList); 

     layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     this.mContext = context; 
     this.trackArrayList = trackArrayList; 
    } 

    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 

     View rowView = convertView; 
     ViewHolder viewHolder; 

     if (rowView == null) { 

      rowView = layoutInflater.inflate(R.layout.track_list_item, null); 

      viewHolder = new ViewHolder(); 
      viewHolder.relativeLayout = (RelativeLayout) rowView.findViewById(R.id.relativeLayout); 
      viewHolder.trackText = new TextView(mContext); 

      viewHolder.trackText.setTextColor(Color.GRAY); 
      viewHolder.trackText.setX(100); 
      viewHolder.trackText.setY(20); 
      viewHolder.trackText.setTextSize(18); 
      viewHolder.relativeLayout.addView(viewHolder.trackText); 


      rowView.setTag(viewHolder); 

     } else { 

      viewHolder = (ViewHolder) rowView.getTag(); 

     } 

     viewHolder.track = trackArrayList.get(position); 

     if (viewHolder.track.getName() == null) 
      viewHolder.trackText.setText(" NUMBER " + position); 
     else 
      viewHolder.trackText.setText(viewHolder.track.getName()); 


     for (int i = 0; i < viewHolder.track.getNumberOfLights(); i++) { 

      Light light = viewHolder.track.lightsOnThisTrackList.get(i); 

      if (light.getParent() != null) { 
       if (!light.getParent().equals(viewHolder.relativeLayout)) { 
        ViewGroup viewGroup = (ViewGroup) light.getParent(); 
        if (viewGroup != null) viewGroup.removeView(light); 
        viewHolder.relativeLayout.addView(light); 
       } 
      } else { 
       viewHolder.relativeLayout.addView(light); 
      } 

     } 
     notifyDataSetInvalidated(); 
     notifyDataSetChanged(); 

     return rowView; 
    } 

    public static class ViewHolder { 
     Track track; 
     TextView trackText; 
     RelativeLayout relativeLayout; 

    } 

    public View getViewByPosition(int pos, ListView listView) { 
     final int firstListItemPosition = listView.getFirstVisiblePosition(); 
     final int lastListItemPosition = firstListItemPosition + listView.getChildCount() - 1; 

     if (pos < firstListItemPosition || pos > lastListItemPosition) { 
      return listView.getAdapter().getView(pos, null, listView); 
     } else { 
      final int childIndex = pos - firstListItemPosition; 
      return listView.getChildAt(childIndex); 
     } 
    } 
} 

回答

1

的問題是不是ViewHolder。問題是,您沒有考慮到您的視圖回收時會發生什麼情況。

假設位置0您將兩個Light s添加到Relativelayout。然後用戶滾動並將視圖回收到另一個位置(假設位置爲10)。在你做任何事情之前,給出的RelativeLayout已經有兩個Light了。

您或者需要先刪除所有先前的Light,或者您需要能夠重新使用那些已有的(並且仍然可能需要刪除一些以防萬一您創建的行較少Light比已經存在的)。

TextView不會重複,因爲您每次回收視圖時都不會創建TextView;您只是在新行被充值時才創建它。


其他一些建議:

  • 應該有沒有理由罵notifyDataSetInvalidated()notifyDataSetChanged()getView()內。
  • 我不鼓勵在數據模型中使用持有名單View(在本例中爲Light)。數據和表示之間沒有明確的分離,我認爲這隻會使代碼複雜化。只需存儲一個Track需要的燈光數量並單獨處理實際的View將會更容易。
  • 我也會盡量避免在getView()內部創建,添加和刪除View。例如,如果您知道Track可以擁有的燈光數量有限(假設爲五),那麼很容易就可以在行佈局中獲得許多相應的視圖,並且只是適當地切換其可見性。或者,您可以自定義View,知道如何繪製該數量的燈光,並且您只需更改getView()中的數字即可。
+0

謝謝,刪除RelativeLayout中的前視圖做了訣竅。另外,正如你設想的那樣,'notifyDataSetInvalidated()'和'notifyDataSetChanged()'在getView()內部是不必要的,只需將它放在我修改數據集的代碼部分即可。 – gotramaval

+0

最後,我在listView中使用這些視圖,因爲我希望能夠滾動它們而不必編碼視圖的滾動。那麼你會如何提出這樣做​​?(我真的不能預見'Track'上會有'Light'的最大數量,並且它們沒有特定的位置,因爲用戶可以沿軌道圖像拖動它們) – gotramaval

+0

@gotramaval I don不知道什麼對你的用例最好,但是我認爲如果你打算讓這些燈可以拖動,那麼'ListView'會讓你的生活變得非常困難。對於觸摸交互控制,「ListView」相當沉重。如果您可以切換到使用'RecyclerView',那會更好。 – Karakuri