2016-06-11 80 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