When you are developing an Android program; and you want to have a ArrayAdapter you can Simply have a Class (most of times with ViewHolder suffix) or directly inflate your convertView and find your view by id.
So What is the benefit of using ViewHolder?
The example of both here :
@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = ((Activity)getContext()).getLayoutInflater().inflate(R.layout.row_phrase, null); } ((TextView) convertView.findViewById(R.id.txtPhrase)).setText("Phrase 01"); }
Or create an inner class in the ArrayAdapter as following:
static class ViewHolder { ImageView leftIcon; TextView upperLabel; TextView lowerLabel; }
and finally in the getView :
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.row_layout, null, false); holder = new ViewHolder(); holder.leftIcon = (ImageView) view.findViewById(R.id.leftIcon); } }
A ViewHolder describes an item view and metadata about its place within the RecyclerView. RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive View. findViewById(int) results.
When you are developing an Android program; and you want to have a ArrayAdapter you can Simply have a Class (most of times with ViewHolder suffix) or directly inflate your convertView and find your view by id.
ViewHolder is a design pattern which can be applied as a way around repeated use of findViewById() . A ViewHolder holds the reference to the id of the view resource and calls to the resource will not be required after you “find” them: Thus performance of the application increases.
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 ).
Understand how listview recycling works
How ListView's recycling mechanism works
You cannot recycle a row that is presently in use. The above link explains how listview recycling mechanism works
So What is the benefit of using ViewHolder?
Quoting docs
Your code might call findViewById()
frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById()
is to use the "view holder" design pattern.
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { // if convertView is null convertView = mInflater.inflate(R.layout.mylayout, parent, false); holder = new ViewHolder(); // initialize views convertView.setTag(holder); // set tag on view } else { holder = (ViewHolder) convertView.getTag(); // if not null get tag // no need to initialize } //update views here return convertView; }
You missed the important part convertView.setTag(holder)
and holder = (ViewHolder) ConvertView.getTag()
http://developer.android.com/training/improving-layouts/smooth-scrolling.html
As you fling through your ListView, there's only a handful of views being shown at any given time. This means that you don't have to instantiate a view for every item in your adapter; when a view scrolls off-screen, it can be reused, or recycled.
View recycling and the ViewHolder pattern are not the same. The ViewHolder pattern is solely to reduce the number of view.findViewById(int)
calls you make. The ViewHolder pattern only works when you take advantage of view recycling.
In getView(int position, View convertView, ViewGroup parent)
, the convertView
parameter is either null or it's a view that has been recycled: it will still have the data from a different list item bound to it.
Without the ViewHolder pattern, you can still take advantage of view recycling (i.e. not blindly instantiating views):
public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { view = // inflate new view } ImageView imageView = (ImageView) view.findViewById(R.id.listitem_image); TextView textView = (TextView) view.findViewById(R.id.listitem_text); TextView timestampView = (TextView) view.findViewById(R.id.listitem_timestamp); ProgressBar progressSpinnerView = (ProgressBar) view.findViewById(R.id.progress_spinner); // TODO: set correct data for this list item // imageView.setImageDrawable(...) // textView.setText(...) // timestampView.setText(...) // progressSpinnerView.setProgress(...) return view; }
Above is an example of view recycling - we do not inflate a new View for each row; we only inflate a view if we're not given one to reuse. Avoiding having to inflate a view is the part that will definitely help with performance when scrolling through your list: take advantage of view recycling.
So, what's the ViewHolder for then? We're currently doing 4x findViewById(int)
for every item, regardless of whether the row itself already existed. As findViewById(int)
recursively iterates down a ViewGroup til it finds a descendent with the given ID, this is a bit pointless for our recycled views - we're re-finding views that we already have references to.
Avoid this by using a ViewHolder object to hold references to the sub-views after you "find" them:
private static class ViewHolder { final TextView text; final TextView timestamp; final ImageView icon; final ProgressBar progress; ViewHolder(TextView text, TextView timestamp, ImageView icon, ProgressBar progress) { this.text = text; this.timestamp = timestamp; this.icon = icon; this.progress = progress; } }
View.setTag(Object)
allows you to tell the View to hold an arbitrary object. If we use it to hold an instance of our ViewHolder after we do our findViewById(int)
calls, then we can use View.getTag()
on recycled views to avoid having to make the calls again and again.
public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { view = // inflate new view ViewHolder holder = createViewHolderFrom(view); view.setTag(holder); } ViewHolder holder = view.getTag(); // TODO: set correct data for this list item // holder.icon.setImageDrawable(...) // holder.text.setText(...) // holder.timestamp.setText(...) // holder.progress.setProgress(...) return view; } private ViewHolder createViewHolderFrom(View view) { ImageView icon = (ImageView) view.findViewById(R.id.listitem_image); TextView text = (TextView) view.findViewById(R.id.listitem_text); TextView timestamp = (TextView) view.findViewById(R.id.listitem_timestamp); ProgressBar progress = (ProgressBar) view.findViewById(R.id.progress_spinner); return new ViewHolder(text, timestamp, icon, progress); }
The performance benefits of this optimisation is questionable, but that's the benefit of the ViewHolder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With