Confused about the ViewHolder and convertView template

I am new to android development and am going through the example code. I copied one method from the example code in the Adapter class (derived from ArrayAdapter), the derived class has a checkbox in addition to the text view:

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

  View listItem = super.getView(position, convertView, parent);

  CheckedTextView checkMark = null;
  ViewHolder holder = (ViewHolder) listItem.getTag();
  if (holder != null) {
    checkMark = holder.checkMark;
  } else {
    checkMark = (CheckedTextView) listItem.findViewById(android.R.id.text1);
    holder = new ViewHolder(checkMark);
    listItem.setTag(holder);
  }

  checkMark.setChecked(isInCollection(position));
  return listItem;
}

private class ViewHolder {
  protected final CheckedTextView checkMark;

  public ViewHolder(CheckedTextView checkMark) {
     this.checkMark = checkMark;
  }
}

      

The sample code is to optimize getView by caching the view in the ViewHolder object.

Where I'm confused, I thought that convertView, if not null, would be reassigned and then the View data is populated into it and returned.

If so, how can the setTag / getTag methods be used in code? It would seem that the same object will need to be found for it to work?

+3


source to share


2 answers


Perhaps the view returned from getTag for later invocation refers to a different list item and returns the wrong view

The adapters use RecycleBin. This class allows the ListView to create only as many row layouts as will fit on the screen, plus one or two for scrolling and preloading. So if you have a ListView with 1000 rows and a screen that only displays 7 rows then chances are that the ListViiew will only have 8 unique views.



Now for your question, using my example above, only eight row layouts and 8 subsequent ViewHolders are created. When users scroll, new row layouts are never created; only the content of the row layout changes. This way, getTag()

there will always be a valid ViewHolder that references the corresponding View (s).

(Does it help?)

+5


source


You are on the right track, here is some information that might help you better understand how ListViews work:

A simple method implementation getView()

has two purposes. The first inflates the view to be displayed in the list. The second fills the view with the data to be shown.

As you said, ListViews retarget the views that make up the list. This is sometimes called species recirculation. The reason for this is scalability. Consider a ListView that contains 1000 items of data. Views can take up a lot of space and it would be impossible to inflate 1000 views and keep them in memory as this can lead to performance hits or scary OutOfMemoryException

. To simplify ListViews, Android uses a method getView()

to marry views with underlying data. As the user scrolls up and down the list, all views that move off the screen are put into a pool of views to be reused. The parameter convertView

getView()

comes from this list. Initially this pool is empty, so null Views are passed togetView()

... So the first part of getView is to check if it was previously convertView

overstated. Also, you want to set up attributes convertView

that will be propagated to all list items. This code will look something like this:

if(convertView == null)
{
    convertView = new TextView(context);
    convertView.setTextSize(28);
    convertView.setTextColor(R.color.black);  
}

      

The second part of the implementation getView()

looks at your underlying data source for the list and sets up that particular view instance. For example, in our test list, we might have an Array of Strings to set the text of a view, and want to set a tag as the current position in the Data of that view. We know which item in the list we are working with based on parmeter position

. This configuration will be as follows.

String listText = myListStringsArray[position];
((TextView)convertView).setText(listText);
convertView.setTag(position);

      



This allows us to minimize the time spent inflating / creating new views, an expensive operation, while still being able to quickly customize each view for display. Putting it all together, your method would look like this:

@Override
public View getView(int position, View convertView, ViewGroup)
{ 
    if(convertView == null)
    {
        convertView = new TextView(context);
        //For more complex views, you may want to inflate this view from a layout file using a LayoutInflator, but I'm going to keep this example simple.

        //And now, configure your View, for example...
        convertView.setTextSize(28);
        convertView.setTextColor(R.color.black);  
    }

    //Configure the View for the item at 'position'
    String listText = myListStringsArray[position];
    ((TextView)convertView).setText(listText);
    convertView.setTag(position);

    //Finally, we'll return the view to be added to the list.

    return convertView;

}

      

As you can see, the ViewHolder is not needed because the OS handles it for you! The views themselves should be considered temporary objects, and any information they need to hold should be manipulated with your underlying data.

Another caveat, the OS doesn't do anything for the views that are pooled as they are, including any data they filled in or the changes they made to them. A well-implemented method getView()

ensures that the underlying data will keep track of any changes in the state of the views. For example, if you change the text color of your TextView to red onClick, when that view is redesigned, the text color will remain red. The text color in this case must be associated with some underlying data and set outside the condition if(convertView == null)

every time it is called getView()

. (Basically, the static customization common to all convertViews happens inside a conditional, dynamic customization based on the current list item, and user input happens after) Hope this helps!

Edited. Made the example simpler and cleaned up the code, thanks Sam!

+5


source







All Articles