Further semantics and practices
Well, I personally think that ListViews are some of the most complicated Views in the Android ecosystem. So, I'm going to make two blog posts talking about them! The first is the ViewHolder vs. HolderView pattern, the second will be some nasty issues on ViewHolder caching I ran into. So, without further ado:
What is ViewHolder?
One of the biggest problems in displaying a ListView in Android is just how inefficient it is. Specifically, calling findViewById() is really expensive, so we try and do everything we can to avoid it. That's why the ViewHolder pattern was invented. The basic principle is:
The
ListViewgives us aViewobject when it requests a view from the adapter withgetView()If that
Viewisnull, we need to inflate a new one. Then, cache it in the view withsetTag()If that
Viewis notnull, we can use the View'sgetTag()method to retrieve a cached version of it (since it's been inflated prior)
So, what we actually cache in that sequence above is the ViewHolder object. Basically, it just stores a static reference to the inflated fields so we don't have to reinflate everything. Then, the actual adapter is responsible for updating the inflated View using the references in the ViewHolder.
If you have any other questions on ViewHolder, check out this page because the author did an amazing job of explaining everything.
What is HolderView?
The HolderView pattern has the same basic principle. We want to make as few calls to findViewById() as possible. There's a big difference though: The HolderView is responsible for its own presentation.
- The
ViewHolderjust stores a reference to theViewelements that are being inflated (by the adapter). TheHolderViewactually inflates itself. - The Adapter driving the ListView is responsible for the presentation of data in each element. The
HolderViewhandles it's own presentation through abind()or some similar method. - The
ViewHolderis a static class, and so can't be garbage collected. EachViewHoldertakes up incredibly little space, but if you're OCD, you still have unused objects you can't remove from memory. TheHolderViewis allowed to go out of scope and be deconstructed.
There are a few downsides too:
- The
ViewHolderpattern is nearly ubiquitous on the Internet. Honestly, if I wasn't forced to useViewHolderby Android Annotations, I would never have known it exists (I can't justify it, but I'm guessing a similar pattern is used on iOS development, feel free to let me know). - The
HolderViewlikely means you'll have another class file. You don't necessarily have to do this, but it's usually better that way. - The
HolderViewis used in place of the view that theListViewis actually trying to inflate. That is, theViewHolderjust caches references to elements inside theView. TheHolderViewactually replaces theView(which means typecasting).
By any means, the HolderView pattern kind of acts like an MVC layer for your views. You can check out my original implementation over here.
So the biggest benefit of using a HolderView pattern is that you can move the presentation logic of a View to its own class, and thus do some pretty cool stuff (for example, Dagger won't let you inject non-static inner classes).
So those are the two patterns! Coming up next: more than you ever wanted to know about ListView caching.