将视图添加到 ListView 内的 RelativeLayout 会产生重复项
Adding views to RelativeLayout inside ListView yields repeated items
我有一个使用自定义 ArrayAdapter 的列表视图。 ListView 的项目是 RelativeLayouts。存储在 "Track" 对象的 "lightsOnThisTrack" 列表中的 "Light" 视图随后被添加到其相应的 RelativeLayouts 中。
问题是,如果我向 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。问题是您没有考虑回收视图时发生的情况。
假设您将两个 Light
添加到 Relativelayout
位置 0
。然后用户滚动并且视图被回收到另一个位置(假设位置 10
)。在你做任何事情之前,给你的RelativeLayout
已经有两个Light
了。
您要么需要先删除所有以前的 Light
,要么您需要能够重新使用那里的那些(但您可能仍然需要删除一些,以防您所在的行重新创建的 Light
比现有的少。
没有重复TextView
,因为你不是在每次回收视图时都创建一个TextView
;您仅在膨胀新行时才创建它。
其他一些建议:
- 应该没有理由在
getView()
. 中调用 notifyDataSetInvalidated()
和 notifyDataSetChanged()
- 我不鼓励在您的数据模型中使用
View
s(在本例中为 Light
s)的持有列表。您没有明确区分数据和表示,我认为这只会使您的代码复杂化。只存储轨道需要多少灯光并单独处理实际的 View
会更容易。
- 我也会尽量避免在
getView()
中创建、添加和删除 View
。例如,如果您知道一个 Track 可以拥有的灯光数量有限(假设是五个),那么在行布局中已经有那么多对应的视图并适当地切换它们的可见性就足够容易了。或者,您可以自定义 View
知道如何绘制该数量的灯,您只需更改 getView()
. 中的数字
我有一个使用自定义 ArrayAdapter 的列表视图。 ListView 的项目是 RelativeLayouts。存储在 "Track" 对象的 "lightsOnThisTrack" 列表中的 "Light" 视图随后被添加到其相应的 RelativeLayouts 中。
问题是,如果我向 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。问题是您没有考虑回收视图时发生的情况。
假设您将两个 Light
添加到 Relativelayout
位置 0
。然后用户滚动并且视图被回收到另一个位置(假设位置 10
)。在你做任何事情之前,给你的RelativeLayout
已经有两个Light
了。
您要么需要先删除所有以前的 Light
,要么您需要能够重新使用那里的那些(但您可能仍然需要删除一些,以防您所在的行重新创建的 Light
比现有的少。
没有重复TextView
,因为你不是在每次回收视图时都创建一个TextView
;您仅在膨胀新行时才创建它。
其他一些建议:
- 应该没有理由在
getView()
. 中调用 - 我不鼓励在您的数据模型中使用
View
s(在本例中为Light
s)的持有列表。您没有明确区分数据和表示,我认为这只会使您的代码复杂化。只存储轨道需要多少灯光并单独处理实际的View
会更容易。 - 我也会尽量避免在
getView()
中创建、添加和删除View
。例如,如果您知道一个 Track 可以拥有的灯光数量有限(假设是五个),那么在行布局中已经有那么多对应的视图并适当地切换它们的可见性就足够容易了。或者,您可以自定义View
知道如何绘制该数量的灯,您只需更改getView()
. 中的数字
notifyDataSetInvalidated()
和 notifyDataSetChanged()