ListView的回收机制如何工作

所以我遇到了这个问题,当然我在这里要求帮助。 Luksprog的回答非常好,因为我不知道ListView和GridView如何通过回收Views来优化自己。 所以他的build议,我能够改变我如何添加视图到我的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; } } 

问题是当我滚动,发生这种情况,而不是位置0 …看起来像位置6和位置8,加上它把两个位置8.现在我仍然试图使用ListView和GridView的挂起,所以我做不明白为什么会这样。 我做这个问题的主要原因之一是帮助其他人可能不知道关于ListView和GridView的回收视图,或本文的方式,ScrapView机制。

在这里输入图像描述

稍后编辑

添加链接到谷歌IO谈话基本上你需要了解如何工作的ListView。 链接在评论死了。 所以user3427079已经足够用来更新这个链接了。 这是为了方便访问。

最初我还不知道listview的回收和convertview使用机制,但经过了一整天的研究,我非常了解列表视图的机制,通过引用android.amberfog 在这里输入图像描述

当你的列表视图充满了一个适配器,它基本上显示列表视图可以显示在屏幕上的行数,行数不会增加,即使你滚动列表,这是android的使用技巧,使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); 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,因为最初在回收站中没有view(item),所以你的getView()为可见项目创build了每个新的视图,但是当你向上滚动你的项目1将发送给Recycler (例如TextView文本和我的情况下,如果checkbox被选中,它也将关联视图并存储在回收站中)。 现在,当你向上/向下滚动你的listview不会创build一个新的视图,它将使用已经在你的回收站的视图(转换视图),在你的Logcat中,你会注意到convertView不是null,因为你的新项目8将使用convertview绘制,也就是说,基本上它是从回收站和inflater代替item 8的item1视图,你可以观察到,如在我的代码,如果你有一个checkbox,如果你检查它在位置0(让说item1也有一个checkbox,你检查它),所以当你向下滚动,你会看到项目8checkbox已经被选中,这就是为什么listview重新使用相同的观点,而不是由于性能优化创build一个新的。

重要的事

1 。 永远不要将你的列表视图的layout_heightlayout_width设置为wrap_content因为getView()会强制你的适配器获得一些用于测量在列表视图中绘制的视图高度的孩子,并且会导致一些意外的行为,比如返回convertview,甚至列表不是滚动。 match_parent使用match_parent或固定的宽度/高度。

2 。 如果你想使用一些布局或查看后列表视图和问题可能出现在你的脑海里,如果我将layout_height设置为fill_parent ,列表视图后的视图将不会显示,因为它下来的屏幕,所以最好把你的列表视图里面的布局。例如线性布局,并根据您的要求设置该布局的高度和宽度,并使您的列表视图的高度宽度属性为您的布局(如果您的布局宽度为320 ,高度为280),然后你的列表视图应该有相同的高度宽度 。 这将告诉getView()的确切的高度和宽度的意见,并getView()不会再次调用一些随机行,和其他问题,如返回转换视图,即使在滚动之前不会发生,我有testing这个我自己,除非我的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 

但现在解决了,我知道,我不擅长解释,但是当我把整整一天的时间都理解了,所以我认为像我这样的其他初学者可以得到我的经验帮助,我希望现在你们会有一点点的理解的ListView框架是如何工作的,因为它真的很混乱,所以初学者发现太多的问题了解它

请注意,在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.. 

使用持有人模式,你可以达到你想要的:

你可以在这里find这个模式的描述:

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