ListView 的回收机制是如何工作的

所以我有这个问题,我以前有,自然我要求帮助对 给你。Luksprog 的回答很棒,因为我不知道 ListView 和 GridView 是如何利用回收视图优化自身的。因此,在他的建议下,我能够更改如何将视图添加到 GridView 中。问题是我现在有些事情说不通。这是我的 getView从我的 BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
convertView = inflater.inflate(R.layout.day_view_item, parent, false);
}
Log.d("DayViewActivity", "Position is: "+position);
((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);


//layout.addView(new EventFrame(parent.getContext()));


TextView create = new TextView(DayViewActivity.this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
create.setLayoutParams(params);
create.setBackgroundColor(Color.BLUE);
create.setText("Test");
//the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
//new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)
if(position == 0) {
Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
layout.addView(create);
}


return convertView;
}


}

问题是当我滚动的时候,会发生这种情况,而不是在位置0... 看起来像位置6和位置8,加上它把2放在位置8。现在我仍然在尝试掌握使用 ListView 和 GridView 的窍门,所以我不明白为什么会发生这种情况。我提出这个问题的主要原因之一是为了帮助那些可能不知道 ListView 和 GridView 的回收视图,或者按照这个 文章的说法,ScrapView 机制的人。

enter image description here

稍后编辑

添加链接到一个谷歌 IO 谈话,这基本上是所有你需要了解 ListView 如何工作。链接完全被这些评论吸引住了。所以 user3427079很好地更新了这个链接。给你这是为了方便访问。

83578 次浏览

最初,我也没有意识到 listview 的循环和转换视图的使用机制,但是经过一整天的研究,我通过参考来自 android.amberfog.com/? p = 296”rel = “ noReferrer”> android.amberfog.com 的图片,基本上理解了列表视图的使用机制 enter image description here

每当您的列表视图被适配器填充时,它基本上就会显示列表视图可以在屏幕上显示的 的数量,而且即使您在列表中滚动,行的数量也不会增加。这是 android 使用的技巧,以便 listview 工作得更有效、更快。 现在,正如你所看到的,参考图片的 listview 的内部故事,最初的 listview 有7个可见的项目,然后,如果你向上滚动直到项目1不再可见,getView ()将这个视图(即 item1)传递给回收者,你可以使用

System.out.println("getview:"+position+" "+convertView);

在你的

public View getView(final int position, View convertView, ViewGroup parent)
{
System.out.println("getview:"+position+" "+convertView);
ViewHolder holder;
View row=convertView;
if(row==null)
{
LayoutInflater inflater=((Activity)context).getLayoutInflater();
row=inflater.inflate(layoutResourceId, parent,false);
        

holder=new PakistaniDrama();
holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);
        

row.setTag(holder);
        

}
else
{
holder=(PakistaniDrama)row.getTag();
}
holder.tvDramaName.setText(dramaList.get(position).getDramaName());
holder.cbCheck.setChecked(checks.get(position));
return row;
}

你会注意到在 logcat 中,最初,对于所有可见的行,Convertview 是 null,因为最初在回收器中没有视图(即项目) ,所以你的 getView ()为每个可见的项目创建了一个新的视图,但是当你向上滚动并且项目1移出屏幕时,它将被发送到 回收站及其当前状态(例如 TextView‘ text’或者在我的例子中,如果选中复选框,它将与视图关联并存储在回收器中)。

现在,当您向上/向下滚动时,您的 listview 不会创建新视图,它将使用回收器中的视图。在你的 日志猫中,你会注意到“ ConvertView”不是空的,它是因为你的新项目8将使用 convertView 来绘制,也就是说,基本上它从回收商那里获取项目1视图,并在它的位置充气项目8,你可以在我的代码中观察到这一点。如果你有一个复选框,并且你在位置0选中了它(假设 item1有一个复选框并且你选中了它) ,那么当你向下滚动时,你会看到第8项已经选中了,这就是为什么 listview 使用相同的视图,而不是为你创建一个新的视图,因为性能优化。

重要的事

1 .永远不要将列表视图的 layout_heightlayout_width设置为 wrap_content,因为 getView()会迫使你的适配器获取一些子元素来测量列表视图中要绘制的视图的高度,并且可能导致一些意想不到的行为,比如即使列表没有滚动,也会返回转换视图。始终使用 match_parent或固定宽度/高度。

2强。如果你想使用一些布局或视图后,你的列表视图和问题可能会出现在你的脑海中,如果我设置为 layout_heightfill_parent的视图后,列表视图将不会显示,因为它下降的屏幕,所以它更好地把你的列表视图内的布局。例如线性布局,设置布局的高度和宽度作为您的要求,使您的列表视图的 身高宽度属性作为您的布局(如果您的布局宽度是 320和高度是 280),那么您的列表视图应该有相同的 身高宽度。这会告诉 getView ()要渲染的视图的确切高度和宽度,而且 getView ()不会一次又一次地调用一些随机行,还有其他问题,比如在滚动之前返回转换视图,这些问题不会发生,我自己已经测试过了,除非我的列表视图在 lineaLayout 内部,否则它也会有重复调用和转换视图的问题,因为把 Listview 放在 LinearLayout 内部对我来说就像魔术一样。(不知道为什么)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

但是现在它解决了,我知道,我不是很擅长解释,但是因为我花了一整天的时间去理解,所以我认为其他像我一样的初学者可以从我的经验中得到帮助,我希望现在你们能对 列表视图框架的工作原理有一点了解,因为它真的很混乱和棘手,所以初学者发现太多的问题理解它

注意,在 Holder 模式中,如果您在 Holder 对象中设置了位置,那么您应该每次都设置它,例如:

@Override
public final View getView(int position, View view, ViewGroup parent) {
Holder holder = null;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) App.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(getContainerView(), parent, false);
holder = getHolder(position, view, parent);
holder.setTag(tag);
view.setTag(holder);
} else {
holder = (Holder) view.getTag();
}
holder.position = position;
draw(holder);
return holder.getView();
}

这是一个来自抽象类的示例,其中

getHolder(position, view, parent);

对象的所有设置操作

ImageViews, TextViews, etc..

使用 Holder 模式,你可以达到你想要的效果:

你可以在这里找到这种模式的描述:

当您向下滚动屏幕并且上面的列表视图项被隐藏时,将发生对列表视图的回收。它们被重用以显示新的列表视图项。