ViewHolder Pattern – Caching View Efficiently
A – Yeah! It works!
Yo, we’ve got a list view to display something:
This is what people like to write for list adapter:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if(v == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
v = inflater.inflate(R.layout.layout_list_item, null);
}
TextView txtName = (TextView)v.findViewById(R.id.txtName);
TextView txtMail = (TextView)v.findViewById(R.id.txtMail);
Contact entry = mList.get(position);
txtName.setText(entry.getName());
txtMail.setText(entry.getMail());
return v;
}
That’s great because it works with no problem and the list displays contents nicely!!!
B – Dig It Out
Let’s see how it works..
+ First time loaded, oh right, it’s null! Ok, to find our TextView controls, ‘findViewById()‘ is loaded. Great, we’ve got it!
+ Second time loaded, it must be not null, but ‘findViewById()‘ is loaded. Well, just one time, it’s fine!
+ Next time loaded, it’s absolutely not null, eh, ‘findViewById()‘ is called by default. Eh oh..
+ ……whatever time
+ xxx th time…..something is calling ‘findViewById()‘…so stupid, damn it! But it’s still running fine … _ _!
C – Giving birth to a child: ViewHolder
You may see the problem, too. When the list is too big, containing lots of rows, then the calling of ‘findViewById()‘ everytime will impact slightly to your application performance, even though, it’s still working fine; however, it’s quite risky since it might be dead at some unexpected point.
This is one of most common problem, it is! Hence, from somewhere, the idea of ViewHolder pattern was created to improve this current situation.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if(v == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
v = inflater.inflate(R.layout.layout_list_item, null);
ViewHolder holder = new ViewHolder();
holder.txtName = (TextView)v.findViewById(R.id.txtName);
holder.txtMail = (TextView)v.findViewById(R.id.txtMail);
v.setTag(holder);
}
Contact entry = mList.get(position);
if(entry != null) {
ViewHolder holder = (ViewHolder)v.getTag();
holder.txtName.setText(entry.getName());
holder.txtMail.setText(entry.getMail());
}
return v;
}
static class ViewHolder {
TextView txtName;
TextView txtMail;
}
The ViewHolder object is static, once created, it will cache all things inside. Therefore, we can avoid calling ‘findViewById()‘ on resource everytime.
D – Note
- Something works doesn’t mean it’s the best. Even if it’s fine, it might contain some invisible problems.
- Use ViewHolder pattern whenever possible to cache data!
E – Final Words
- Just something I’ve learned and wanna share to everyone.
- Feel free to comment, suggest and request; I’m here to listen to everyone’s voice
- Free to share, free to take!
Cheers,
Pete Houston

I made a tool to automatically generate code for a ViewHolder given an XML Layout. Check it out here: http://www.buzzingandroid.com/tools/android-layout-finder/
Is this ViewHolder really useful and are its improvements really impacting performance? Is findViewById so resource-consuming? I don’t see any benefit at the moment…
so actually if i have a custom ViewGroup like
com.mystuff.MyViewGroup extends LinearLayout
with the same variables you declared in your example…
i don’t need to to use the viewholder pattern? like
void MyViewGroup (){
txtName = (TextView)v.findViewById(R.id.txtName);
…
}
Yes, you still need ViewHolder pattern. YourViewGroup is just an extended View no more no less. Always refer or keep your stuffs `static` somewhere to be resided in memory for cache.
what if the view is a custom view (group) that stores the datamodel itself, is it ok just to add the view to the viewHolder (the view can retain the references to its children)
for instance i have a customlinearLayout class that has a chackbox and a textview, in the constructor i already do a findViewById() and get the datamodel…so storing the whole view in the ViewHolder seems to be ok…isn’t it?
Yes, it’s ok, no problem at all.
I am getting an error on Contact entry = mList.get(position); It tells me that Contact cannot be resolved as a type.
And..
holder.txtName = (TextView)v.findViewById(R.id.txtName);
holder.txtMail = (TextView)v.findViewById(R.id.txtMail); //error on the TextView here
I’ve been working on this for a while and i really cant figure it out… I’m sure its somethings small that im overlooking im still pretty new at this concept. Thanks in advanced
I FIGURED IT OUT!!!! IT was a newbie mistake.. THANKS FOR THE TUTORIAL!!!
whats was the issue with Contact i am having the same issue. i just started development on android so i am pretty new to this stuff.
So great! Thanks a lot
Very useful with me ^^