So I have this problem I had before, and naturally I asked for help on here. Luksprog's answer was great because I had no idea about how ListView and GridView optimized itself with recycling Views. So with his advice I was able to change how I added Views to my GridView. Problem is now I have something that does not make sense. This is my getView
from my BaseAdapter
:
所以我遇到了这个问题,很自然地,我在这里寻求帮助。Luksprog的回答很好,因为我不知道ListView和GridView是如何利用回收视图优化自身的。因此,通过他的建议,我能够改变我如何向GridView添加视图的方式。现在的问题是我有一些不合理的东西。这是来自我的BaseAdapter的getView:
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;
}
}
Problem is when I scroll, this happens, and not on position 0... Looks like position 6 and position 8, plus it puts two in position 8. Now I am still trying to get the hang of using ListView and GridView so I do not understand why this is happening. One of the main reasons I am making this question is to help others who probably don't know about ListView and GridView's recycling View, or the way this article puts it, ScrapView mechanism.
问题是当我滚动的时候,会发生这种情况,而不是在0位置。看起来像位置6和位置8,加上位置8的2。现在我还在尝试使用ListView和GridView,所以我不明白为什么会这样。我提出这个问题的一个主要原因是为了帮助那些可能不了解ListView和GridView的回收视图的人,或者像本文所说的那样,帮助他们了解剪贴视图机制。
Later Edit
后编辑
Adding link to a google IO talk that is basically all you need to understand how ListView works. Link was dead in on of the comments. So user3427079 was nice enough to update that link. Here it is for easy access.
将链接添加到谷歌IO会话中,这基本上就是了解ListView如何工作所需要的。林克对这些评论一窍不通。user3427079很适合更新这个链接。这里是方便存取的。
3 个解决方案
#1
301
Initially I was also unaware of listview recycling and the convertview usage mechanism, but after a whole days research I pretty much understand the mechanisms of the list view by referring to an image from android.amberfog
一开始我也不知道listview的回收和convertview的使用机制,但是经过一整天的研究,我通过引用android.amberfog中的一个图像来理解list视图的机制
When ever your listview is filled with an adapter it basically shows the number of Rows that the listview can show on screen and the number of rows doesnt get increase even you scroll through the list, this is the trick android use so that listview work more effcient and fast, Now the inside story of listview refering to image as you can see initially list view has 7 visible items, if you scroll up when item 1 is not any more visible getView() pass this view(item1) to recycler and you can use
当listview充满适配器它listview显示的行数,可以显示在屏幕上的行数不增加甚至是你滚动列表,这是技巧android使用列表视图工作更有效率和快速,现在里面的故事listview参照图片可以看到最初列表视图7可见的物品,如果你向上滚动当项目1是不再可见getView()通过这个视图(item1)回收商,你可以使用
System.out.println("getview:"+position+" "+convertView);
inside your
在你
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;
}
you will notice in your logcat initially convertview is null for all the visible rows, because initially there were no view(item) in recycler, so your getView() creates each new view for the visible items, but the moment you scroll up your item 1 will send to the Recycler with it state(for example the TextView text and as in mine case if checkbox is checked it will also be associated with the view and store in recycler). Now when you scroll up/down your listview is not going to create a new view it will use the view(convert view) which is already in your recycler, in your Logcat you will notice that convertView is not null, its because your new item 8 will be drawn using convertview, i.e., basically it take the item1 view from the recycler and inflater in place of item 8, and you can observe that as in mine code if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked, this is why listview is re using the same view not creating a new for you due to performance optimization.
最初你会注意到logcat convertview所有可见的行是空的,因为一开始没有视图(项目)的回收商,所以getView()创建的每个新视图可见的东西,但当你向上滚动你的第一项将发送到回收商的状态(例如TextView文本和在我的情况下如果复选框选中它也将与视图和存储相关的回收商)。现在当你向上/向下滚动你的listview时它不会创建一个新的视图它会使用视图(转换视图)它已经在你的回收器中,在你的Logcat中你会注意到convertView不是null,因为你的新项目8会用convertView绘制,也就是。,基本上item1视图回收商和增压泵的8项,你可以观察到在我的代码中如果你有一个复选框,如果你检查它在位置0(让说item1还你选中一个复选框)所以当你向下滚动,您将看到项目8复选框已经检查,这就是为什么listview重新使用相同的观点不是为你创建一个新的性能优化。
Important things
重要的事情
1. Never set the layout_height
and layout_width
of your listview to wrap_content
as getView()
will force your adapter to get some child for measuring the height of the views to be drawn in list view and can cause some unexpected behaviour like returning convertview even the list is not scrolled.always use match_parent
or fixed width/height.
1。永远不要将listview的layout_height和layout_width设置为wrap_content,因为getView()将迫使您的适配器获得一些子节点来度量要在listview中绘制的视图的高度,并可能导致一些意想不到的行为,比如返回convertview,即使列表没有被滚动。始终使用match_parent或固定的宽度/高度。
2. If you want to use some Layout or view after your list view and question might came in your mind if i set the layout_height
to fill_parent
the view after list view will not show up as it goes down the screen, so its better to put your listview inside a layout.For example Linear Layout and set the height and width of that layout as of your requirement and make the height and width attribute of your listview to as of your layout(like if your layout width is 320 and height is 280) then your listview should have same height and width. This will tell getView() of exact height and width of views to be rendered, and getView() won't call again and again some random rows, and other problems like returning convert view even before scrolling won't happen, i have test this myself, unless my listview was inside the lineaLayout it was also having problems like repeating view call and convert view as, putting Listview inside LinearLayout worked like magic for me.(didn't know why)
2。如果你想使用一些布局或视图列表视图后,问题可能出现在你介意我设置layout_height宽和视图列表视图后不会出现下降的屏幕,所以它的更好的把你的列表视图内的布局。例如线性布局和设置高度和宽度的布局你的需求,让你的高度和宽度属性视图的布局(就像如果你的布局宽度是320,高度280)那么你的列表视图应该有相同的高度和宽度。这将告诉getView()的高度和宽度的观点呈现,和getView()不会调用一次又一次的一些随机的行,和其他问题,如返回转换视图滚动不会发生之前,我已经测试这个自己,除非我listview 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
But now its solved, I know, I'm not that good at explaining but as i put my whole day to understand so i thought other beginners like me can get help of my experience and i hope now you people will have a little bit understanding of ListView framework how it works, as it is really messy and tricky so beginners found too much problem understanding it
但现在解决了,我知道,我不擅长解释,但当我把我的一整天理解所以我想其他像我这样的初学者可以得到帮助的我的经验,我希望现在你人会有一点ListView框架的理解它是如何起作用的,因为它确实是混乱和复杂所以初学者发现太多的问题理解它
#2
8
Take care, in the Holder pattern, if you set the postion in your Holder object, you should set it every time , for example :
注意,在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();
}
this is an example from an abstract class, where
这是一个抽象类的例子
getHolder(position, view, parent);
does all the setting operations for the
是否所有的设置操作
ImageViews, TextViews, etc..
#3
2
Using the Holder pattern you can achieve what you want:
使用持有者模式你可以实现你想要的:
You can find description of this pattern here:
你可以在这里找到这个模式的描述:
Recycling of list view happens when you scroll down the screen and above list view items are hidden . They are reused to show new list view items.
当您向下滚动屏幕时,列表视图会重新循环,而上面的列表视图项是隐藏的。它们被重用以显示新的列表视图项。
#1
301
Initially I was also unaware of listview recycling and the convertview usage mechanism, but after a whole days research I pretty much understand the mechanisms of the list view by referring to an image from android.amberfog
一开始我也不知道listview的回收和convertview的使用机制,但是经过一整天的研究,我通过引用android.amberfog中的一个图像来理解list视图的机制
When ever your listview is filled with an adapter it basically shows the number of Rows that the listview can show on screen and the number of rows doesnt get increase even you scroll through the list, this is the trick android use so that listview work more effcient and fast, Now the inside story of listview refering to image as you can see initially list view has 7 visible items, if you scroll up when item 1 is not any more visible getView() pass this view(item1) to recycler and you can use
当listview充满适配器它listview显示的行数,可以显示在屏幕上的行数不增加甚至是你滚动列表,这是技巧android使用列表视图工作更有效率和快速,现在里面的故事listview参照图片可以看到最初列表视图7可见的物品,如果你向上滚动当项目1是不再可见getView()通过这个视图(item1)回收商,你可以使用
System.out.println("getview:"+position+" "+convertView);
inside your
在你
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;
}
you will notice in your logcat initially convertview is null for all the visible rows, because initially there were no view(item) in recycler, so your getView() creates each new view for the visible items, but the moment you scroll up your item 1 will send to the Recycler with it state(for example the TextView text and as in mine case if checkbox is checked it will also be associated with the view and store in recycler). Now when you scroll up/down your listview is not going to create a new view it will use the view(convert view) which is already in your recycler, in your Logcat you will notice that convertView is not null, its because your new item 8 will be drawn using convertview, i.e., basically it take the item1 view from the recycler and inflater in place of item 8, and you can observe that as in mine code if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked, this is why listview is re using the same view not creating a new for you due to performance optimization.
最初你会注意到logcat convertview所有可见的行是空的,因为一开始没有视图(项目)的回收商,所以getView()创建的每个新视图可见的东西,但当你向上滚动你的第一项将发送到回收商的状态(例如TextView文本和在我的情况下如果复选框选中它也将与视图和存储相关的回收商)。现在当你向上/向下滚动你的listview时它不会创建一个新的视图它会使用视图(转换视图)它已经在你的回收器中,在你的Logcat中你会注意到convertView不是null,因为你的新项目8会用convertView绘制,也就是。,基本上item1视图回收商和增压泵的8项,你可以观察到在我的代码中如果你有一个复选框,如果你检查它在位置0(让说item1还你选中一个复选框)所以当你向下滚动,您将看到项目8复选框已经检查,这就是为什么listview重新使用相同的观点不是为你创建一个新的性能优化。
Important things
重要的事情
1. Never set the layout_height
and layout_width
of your listview to wrap_content
as getView()
will force your adapter to get some child for measuring the height of the views to be drawn in list view and can cause some unexpected behaviour like returning convertview even the list is not scrolled.always use match_parent
or fixed width/height.
1。永远不要将listview的layout_height和layout_width设置为wrap_content,因为getView()将迫使您的适配器获得一些子节点来度量要在listview中绘制的视图的高度,并可能导致一些意想不到的行为,比如返回convertview,即使列表没有被滚动。始终使用match_parent或固定的宽度/高度。
2. If you want to use some Layout or view after your list view and question might came in your mind if i set the layout_height
to fill_parent
the view after list view will not show up as it goes down the screen, so its better to put your listview inside a layout.For example Linear Layout and set the height and width of that layout as of your requirement and make the height and width attribute of your listview to as of your layout(like if your layout width is 320 and height is 280) then your listview should have same height and width. This will tell getView() of exact height and width of views to be rendered, and getView() won't call again and again some random rows, and other problems like returning convert view even before scrolling won't happen, i have test this myself, unless my listview was inside the lineaLayout it was also having problems like repeating view call and convert view as, putting Listview inside LinearLayout worked like magic for me.(didn't know why)
2。如果你想使用一些布局或视图列表视图后,问题可能出现在你介意我设置layout_height宽和视图列表视图后不会出现下降的屏幕,所以它的更好的把你的列表视图内的布局。例如线性布局和设置高度和宽度的布局你的需求,让你的高度和宽度属性视图的布局(就像如果你的布局宽度是320,高度280)那么你的列表视图应该有相同的高度和宽度。这将告诉getView()的高度和宽度的观点呈现,和getView()不会调用一次又一次的一些随机的行,和其他问题,如返回转换视图滚动不会发生之前,我已经测试这个自己,除非我listview 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
But now its solved, I know, I'm not that good at explaining but as i put my whole day to understand so i thought other beginners like me can get help of my experience and i hope now you people will have a little bit understanding of ListView framework how it works, as it is really messy and tricky so beginners found too much problem understanding it
但现在解决了,我知道,我不擅长解释,但当我把我的一整天理解所以我想其他像我这样的初学者可以得到帮助的我的经验,我希望现在你人会有一点ListView框架的理解它是如何起作用的,因为它确实是混乱和复杂所以初学者发现太多的问题理解它
#2
8
Take care, in the Holder pattern, if you set the postion in your Holder object, you should set it every time , for example :
注意,在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();
}
this is an example from an abstract class, where
这是一个抽象类的例子
getHolder(position, view, parent);
does all the setting operations for the
是否所有的设置操作
ImageViews, TextViews, etc..
#3
2
Using the Holder pattern you can achieve what you want:
使用持有者模式你可以实现你想要的:
You can find description of this pattern here:
你可以在这里找到这个模式的描述:
Recycling of list view happens when you scroll down the screen and above list view items are hidden . They are reused to show new list view items.
当您向下滚动屏幕时,列表视图会重新循环,而上面的列表视图项是隐藏的。它们被重用以显示新的列表视图项。