在ListView中添加视图到RelativeLayout产生重复的项目
问题描述:
我有一个使用自定义ArrayAdapter的列表视图。 ListView的项目是RelativeLayouts。存储在“Track”对象的“lightsOnThisTrack”列表中的“Light”视图随后被添加到其相应的RelativeLayouts中。在ListView中添加视图到RelativeLayout产生重复的项目
问题是,如果我向ListView添加更多项目,以前添加到relativeLayouts的视图开始在新添加的项目上重复。另一方面,TextView“trackText”没有被重复,正如在示例中可以看到的那样。正如我在其他帖子上看到的,我知道这是与ViewHolder模式实现方式有关的问题,但我无法找到问题所在。
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);
}
}
}
答
的问题是不是ViewHolder。问题是,您没有考虑到您的视图回收时会发生什么情况。
假设位置0
您将两个Light
s添加到Relativelayout
。然后用户滚动并将视图回收到另一个位置(假设位置为10
)。在你做任何事情之前,给出的RelativeLayout
已经有两个Light
了。
您或者需要先删除所有先前的Light
,或者您需要能够重新使用那些已有的(并且仍然可能需要删除一些以防万一您创建的行较少Light
比已经存在的)。
TextView
不会重复,因为您每次回收视图时都不会创建TextView
;您只是在新行被充值时才创建它。
其他一些建议:
- 应该有没有理由骂
notifyDataSetInvalidated()
和notifyDataSetChanged()
的getView()
内。 - 我不鼓励在数据模型中使用持有名单
View
(在本例中为Light
)。数据和表示之间没有明确的分离,我认为这只会使代码复杂化。只需存储一个Track需要的灯光数量并单独处理实际的View
将会更容易。 - 我也会尽量避免在
getView()
内部创建,添加和删除View
。例如,如果您知道Track可以拥有的灯光数量有限(假设为五),那么很容易就可以在行布局中获得许多相应的视图,并且只是适当地切换其可见性。或者,您可以自定义View
,知道如何绘制该数量的灯光,并且您只需更改getView()
中的数字即可。
谢谢,删除RelativeLayout中的前视图做了诀窍。另外,正如你设想的那样,'notifyDataSetInvalidated()'和'notifyDataSetChanged()'在getView()内部是不必要的,只需将它放在我修改数据集的代码部分即可。 – gotramaval
最后,我在listView中使用这些视图,因为我希望能够滚动它们而不必编码视图的滚动。那么你会如何提出这样做?(我真的不能预见'Track'上会有'Light'的最大数量,并且它们没有特定的位置,因为用户可以沿轨道图像拖动它们) – gotramaval
@gotramaval I don不知道什么对你的用例最好,但是我认为如果你打算让这些灯可以拖动,那么'ListView'会让你的生活变得非常困难。对于触摸交互控制,“ListView”相当沉重。如果您可以切换到使用'RecyclerView',那会更好。 – Karakuri