2
public View getView(int index, View view, ViewGroup parent){
    if (view == null) { // for the first time, inflate the view
        LayoutInflater inflater =
            LayoutInflater.from(parent.getContext());
        view = inflater.inflate(
            R.layout.time_list_item, parent, false);
    }
    /**
    * The layout under consideration has two TextViews in it
    * one to display the time and other to display some text.
    */
    TimeRecord time = times.get(index);
    timeTextView.setText(time.getTime());
    TextView timeTextView = (TextView)
    view.findViewById(R.id.time_view);
    notesTextView.setText(time.getNotes());
    TextView notesTextView = (TextView)
    view.findViewById(R.id.notes_view);

    return view;
}  

I know that getView() is called repeatedly for every item of the collection that is to be displayed. The reference I am using says that to optimize performance, you should just reuse the view. Now, this 'reuse' is confusing me.

These returned views will be displayed in the ListView. If I ever only return one View that I re-populate with new data, how will the data be properly displayed ? How can there be multiple entries ?
In other words, am I not just returning a single view and expecting to see multiple ListView entries ? Shouldn't it be that I return new Views ?

An SO User
  • 24,612
  • 35
  • 133
  • 221

3 Answers3

3

How does getView() work in a custom adapter?

getView() method is called as many times as is number of rows e.q. each row has own View.

Now, this 'reuse' is confusing me.

It's called View recycling. In other words, if row is not visible, its not null (if was created and visible at least once) but if you won't create mechanism that will hold child views of row, recycling wont work and in your case your findViewById() will be called for each row (in the case of for example 1000 rows, its not very efficient).

For this purpose is used Holder design-pattern. It's simple arbitrary object that holds references of child views of each row.

You can implement it like:

public class RowHolder {

   private View row;

   // childs widgets in row
   private TextView name;

   public RowHolder(View row) {
      this.row = row; // row in ListView (in your case)
   }

   public TextView getName() {
      if (name == null) {
         name = (TextView) row.findViewById(<id>);
      }
      return name;
   }

   ...
}

And a usage:

LayoutInflater inflater;
RowHolder holder = null;

// row created first time
if (convertView == null) {
   convertView = inflater.inflate(<rowXMLLayout>, null, false);
   holder = new RowHolder(convertView); // adding row to arbitrary obj
   convertView.setTag(holder) // adding this obj to row at position
}
else {
  // recycling started
  holder = (RowHolder) convertView.getTag();
}


// updating rows
holder.getName().setText(<value?>);
...
Simon Dorociak
  • 33,374
  • 10
  • 68
  • 106
1

This behavior in ListView named Recycling , you can have more information on How ListView's recycling mechanism works and View Recycling in ListView

Community
  • 1
  • 1
Arash GM
  • 10,316
  • 6
  • 58
  • 76
1

So ListView uses your custom Adapter in such way:

  • It inflates views which are visible + few which are out of screen, to be prepared for showing them later.
  • It inflates views by calling your method getView().
  • Results is that the view is saved to Weak referenced array inside of ListView - meaning view may be removed from it by garbage collector when app is running out of memory.

Advantage is that if you properly reuse RECYCLED VIEW in your getView, then command inflate() will be invoked sometimes only once (but not for every list item, when it's not necessary ). And this inflating is what we want to omit, because it's very slow.

And this array has as much entrances as your custom adapter returns in getViewTypeCount(). In simple listviews it will be just 1. That's why when doing this recycling in getView(int index, View recycledView, ViewGroup parent) you will be getting as parameter the same recycledView every time, which you have to USE = omit another inflating and CHANGE its properties like textViews, images, etc, so it contains stuff which you want. In other words so every item of list doesn't look the same, but just similar, because it has same layout (literally same layout file and same layout like margins, width, etc)

If you have use in Listview few views, meaning once you inflate 1 view, other time different. Ten you should override getViewTypeCount() and getItemViewType() so Listview knows which index of list should get which view.

Malachiasz
  • 7,126
  • 2
  • 35
  • 49