Search code examples
androidlistviewrecycle

Understanding the ViewHolder pattern


I created an adapter for a ListView which has two types of rows. The ListView has 4 rows. The last row has a different layout, that's why i use the GetItemViewType method in getview

I'm trying to understand how the pattern works. I watched this: https://www.youtube.com/watch?v=bWsWe9T9HJw to get a better understanding of how recycling works

What i don't understand is: when i scroll down in my listview, the convertview is always null. When i scroll back up again, the convertview is not null and is reusable.

Shouldn't convertview be null only for the first item in the list? i don't get why it is null for each new item?

public override View GetView (int position, View convertView, ViewGroup parent)
    {
        BaseBundelVO bundle = _bundles [position];

        DSBundleListItem bundleHolder = null;
        DSBundleArchiveItem archiveHolder = null;

        int type = GetItemViewType(position);
        if (convertView == null)
        {
            bundleHolder = new DSBundleListItem (_activity);
            archiveHolder = new DSBundleArchiveItem (_activity);

            switch (type) 
            {
            case 0:
                convertView = _activity.LayoutInflater.Inflate (Resource.Layout.dsBundleListItem, null);
                bundleHolder.IconIv = convertView.FindViewById<ImageView> (Resource.Id.iconIv);
                bundleHolder.CoverIv = convertView.FindViewById<ImageView> (Resource.Id.coverIv);
                bundleHolder.CoverTitleTv = convertView.FindViewById<TextView> (Resource.Id.coverTitleTv);
                bundleHolder.CoverSubTitleTv = convertView.FindViewById<TextView> (Resource.Id.coverSubTitleTv);
                bundleHolder.BundleProgress = convertView.FindViewById<ProgressBar> (Resource.Id.bundleProgress);
                convertView.Tag = bundleHolder;
                break;
            case 1:
                convertView = _activity.LayoutInflater.Inflate (Resource.Layout.dsBundleArchiveItem, null);
                archiveHolder.ArchiveTitleTv = convertView.FindViewById<TextView> (Resource.Id.archiveTitleTv);
                archiveHolder.ArchiveSubTitleTv = convertView.FindViewById<TextView> (Resource.Id.archiveSubTitleTv);
                convertView.Tag = archiveHolder;
                break;
            }

        } 
        else 
        {
            switch (type) 
            {
            case 0:
                bundleHolder = (DSBundleListItem)convertView.Tag;
                Console.WriteLine (bundleHolder.IsDisposed ());
                bundleHolder.RemoveImageLoaderCallBack ();
            break;
            case 1:
                archiveHolder = (DSBundleArchiveItem)convertView.Tag;
                Console.WriteLine (archiveHolder.IsDisposed ());
                archiveHolder.RemoveImageLoaderCallBack ();
            break;
            }
        }

        switch (type) 
        {
        case 0:
            bundleHolder.CoverTitleTv.Text = bundle.Title;
            bundleHolder.CoverSubTitleTv.Text = bundle.SubTitle;
            bundleHolder.LoadImage(bundle.CoverImageLocation,bundle.Icon);
            break;
        case 1:
            archiveHolder.ArchiveTitleTv.Text    = "Archief";
            archiveHolder.ArchiveSubTitleTv.Text = "Bekijk onze eerder verschenen publicaties";
            break;
        }

        return convertView;
    }

Solution

  • Shouldn't convertview be null only for the first item in the list?

    Not usually.

    Let's suppose that that 8 rows are visible in the ListView. That means the ListView will call getView() at least 8 times with null for the convertView parameter, to populate the visible space in the ListView.

    ListView may also cache a few additional rows, to be able to rapidly respond to scrolling events, as a cache.

    Plus, in your case, there are separate object pools maintained for each view type.

    If your adapter has enough stuff in it, though, eventually you will recycle even during the initial scroll down. It all depends on the size of the rows, the values that the adapter returns from getCount(), etc.

    And note that this has nothing really to do with the view holder pattern.