ListView 的回收机制是如何工作的 [英] How ListView's recycling mechanism works

查看:37
本文介绍了ListView 的回收机制是如何工作的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我之前遇到过这个问题,很自然地我在 是为了方便访问.

解决方案

一开始我也不知道listview的回收和convertview的使用机制,但是经过一整天的研究,通过参考,我对listview的机制基本了解了到来自 android.amberfog 的图片

每当您的列表视图被适配器填充时,它基本上会显示列表视图可以在屏幕上显示的数,并且即使您滚动列表,行数也不会增加.这是 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 支架;查看行=convertView;如果(行==空){LayoutInflater inflater=((Activity)context).getLayoutInflater();row=inflater.inflate(layoutResourceId, parent,false);持有人=新的巴基斯坦戏剧();holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);row.setTag(holder);}别的{持有人=(巴基斯坦戏剧)row.getTag();}holder.tvDramaName.setText(dramaList.get(position).getDramaName());holder.cbCheck.setChecked(checks.get(position));返回行;}

您会在 logcat 中注意到,最初,所有可见行的 convertview 为 null,因为最初回收器中没有视图(即项目),因此您的 getView() 为每个可见项目创建了一个新视图,但是当您向上滚动并且项目 1 移出屏幕时,它将以其当前状态发送到 Recycler(例如 TextView 'text' 或在我的情况下,如果复选框是勾选后,会关联到view并存入recycler)

现在,当您向上/向下滚动时,您的列表视图不会创建新视图,它将使用回收器中的视图.在您的 Logcat 中,您会注意到convertView"不为空,这是因为您的新项目 8 将使用 convertview 绘制,即,基本上它从回收器获取项目 1 视图并将项目 8 膨胀到它的位置,你可以在我的代码中观察到.如果您有一个复选框并且您在位置 0 处选中它(假设 item1 有一个复选框并且您选中了它),那么当您向下滚动时,您将看到 item 8 复选框已被选中,这就是 listview 重新使用相同视图的原因,由于性能优化,不会为您创建新的.

重要事项

1.切勿将列表视图的 layout_heightlayout_width 设置为 wrap_content,因为 getView() 会强制您的适配器获取一些child 用于测量要在列表视图中绘制的视图的高度,并且可能导致一些意外行为,例如即使列表未滚动也返回 convertview.始终使用 match_parent 或固定宽度/高度.

2.如果你想在你的列表视图之后使用一些布局或视图,如果我将 layout_height 设置为 fill_parent 列表视图之后的视图将不会显示在你的脑海中当它沿着屏幕向下时,最好将您的列表视图放在布局中.例如线性布局并根据您的要求设置该布局的高度和宽度,并使 height您的列表视图的宽度 属性与您的布局相同(例如,如果您的布局宽度为 320,高度为 280),那么您的列表视图应该具有相同的 高度宽度.这将告诉 getView() 要呈现的视图的确切高度和宽度,并且 getView() 不会一次又一次地调用一些随机行,并且其他问题,例如在滚动之前返回转换视图也不会发生,我有测试这就是我自己,除非我的 listview 在 lineaLayout 内,否则它也有问题,例如重复视图调用和将视图转换为,将 Listview 放在 LinearLayout 内对我来说就像魔术一样.(不知道为什么)

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

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

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:


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.

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.

解决方案

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

Whenever your listview is filled by an adapter it basically shows the number of Rows that the listview can show on screen and the number of rows doesn't increase even when you scroll through the list. This is the trick android uses so that listview works more efficiently and fast. Now the inside story of listview referring to the image, as you can see, initially the listview has 7 visible items, then, if you scroll up until item 1 is no longer visible, getView() passes this view (i.e item1) to the recycler and you can use

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 views (i.e items) in the recycler, so your getView() creates a new view for each of the visible items, but the moment you scroll up and item 1 moves out of the screen, it will be sent to the Recycler with its present state (for example the TextView 'text' or in mine case, if checkbox is checked, it will be associated with the view and stored in recycler).

Now when you scroll up/down, your listview is not going to create a new view, it will use the view which is in your recycler. In your Logcat you will notice that the 'convertView' is not null, its because your new item 8 will be drawn using convertview, i.e., basically it takes item 1 view from the recycler and inflates item 8 in its place, and you can observe that in my code. If you had a checkbox and if you check it at position 0(let's say item1 had a checkbox and you checked it) so when you scroll down you will see item 8 checkbox already checked, this is why listview is re using the same view, not creating a new for you due to performance optimization.

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.

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)

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 的回收机制是如何工作的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆