How Listview'S Recycling Mechanism Works

How ListView's recycling mechanism works

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
Sample Image

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

How ListView's recycling works

list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parentAdapter, View view, int position, long id) {
adapter.changeVisable(view, position);


public void changeVisable(View view, int position) {
if (mLastView != null && mLastPosition != position) {
ListViewHolder holder = (ListViewHolder) mLastView.getTag();
switch (holder.getTxt().getVisibility()) {
case View.VISIBLE:
holder.getTxt().setVisibility(View.GONE);
mLastVisibility = View.GONE;
break;
default:
break;
}
}
mLastPosition = position;
mLastView = view;
ListViewHolder holder = (ListViewHolder) view.getTag();
switch (holder.getTxt().getVisibility()) {
case View.GONE:
holder.getTxt().setVisibility(View.VISIBLE);
mLastVisibility = View.VISIBLE;
break;
case View.VISIBLE:
holder.getTxt().setVisibility(View.GONE);
mLastVisibility = View.GONE;
break;
}
}

works perfect

Does ListView automatically recycle views?

RecyclerView does recycling automatically. In order to make ListView recycle items you will need to do this modification inside of adapter class.

 @Override
public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;

if (convertView == null) {
//brand new
convertView = LayoutInflater.from(mContext).inflate(R.layout.days_list_item, null);
holder = new ViewHolder();

// below is variables that will be different in your case
holder.numberOfDays = (TextView) convertView.findViewById(R.id.eventDays);
holder.sinceOrUntil = (TextView) convertView.findViewById(R.id.eventType);
holder.eventTitle = (TextView) convertView.findViewById(R.id.eventTitle);
holder.daysText = (TextView) convertView.findViewById(R.id.DaysText);

convertView.setTag(holder);
}
else {
//reusing item
holder = (ViewHolder) convertView.getTag();
}

// rest of the code
}

For more details refer to this link.

List View Item Recycling

Change getView to

 @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
FindMyBeerPager findMyBeerPager;
if(convertView==null){
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.beer_list_item, parent,false);
holder.beerPager = (ViewPager)convertView.findViewById(R.id.mypager);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}

findMyBeerPager = new FindMyBeerPager(context, findBeerDataList.get(position));
holder.beerPager.setTag(findMyBeerPager);
holder.beerPager.setAdapter(findMyBeerPager);
return convertView;
}
}

You can move LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); to the constructor of adapter class.

Also use Log for logging instead of System.out.println

How ListView's recycling mechanism works

ViewPager and OnItemClickListener in ListView

Check the answer by Dianne Hackborn in the above link.

Listview row requestLayout() and View recycling

I found my specific problem.

After long debugging through the source of the ListView I found the difference in behavior between my two ListView implementations. I found out that one of them was calling onSizeChanged(), first to a new height immediately followed by another onSizeChanged() with the old height. In this method it sets a flag mDataChanged which in turn will cause the views to be recycled.

In my other ListView i have fixed row heights so it never has this problem. I'm going to do the same for this situation as i don't really need different row heights.



Related Topics



Leave a reply



Submit