Android: Use the ViewHolder Luke, and Larry, and Curly, and Moe

來源:互聯網
上載者:User

This is going to be a quick hit blog post about the Android
"ViewHolder" pattern. This is a pattern that many people seem to be at
least vaguely familiar with, but not very many actually use (based on
open source Android applications and examples/books, etc.).

First some background. Larry, Moe, and Curly were characters from . .
. ok, not that much background, but to really make use of the
ViewHolder with Android you do need to know what the ListView widget is (a helper for managing views of lists), and that ListViews and generally backed by Adapters
(adapters provide data for lists and build views for said data, they
can be made from lists of stuff in files, or from in memory arrays, or
from databases, and so on).

An example of a ListView in action is seen in the screen shot below.

This screen is taken from the DevNexus 2010
demo application I wrote for a conference I attended and spoke at this
year. This particular ListView is basically a navigable list of the
presentation abstracts and speakers. We will look at the code behind
this screen, complete with ViewHolder, coming up. First a bit more
background.

You are probably at least vaguely familiar with ListView/Adapter and
the related concepts if you have done any Android development at all.
ListView is *very* powerful and helpful widget. Nevertheless, ListView's
are often misused and abused because there are so many options and
settings and patterns that surround them. I don't have time to go into
great depth on ListView itself, but for the quick hit I promised I want
to address 2 key things to keep in mind when working with ListViews:

  • Creating views is expensive, you don't want to inflate or manually
    create new views for every view on the list. ListView can re-use views,
    if you let it.
  • Even if you are re-using views, you also don't want to find child
    views by id (findViewById) every time, because that is expensive too.
  • I would say that 90% of Android examples or open source apps I see
    (or developers I talk to) do the first item there properly. Depending on
    the adapter in play they are re-using views. It's pretty simple, use
    the "convertView" on simple adapters like ArrayAdapter (which we will
    see coming up) and use the "newView" on more involved adapters like
    CursorAdapter. I would also say that the same high percentage of
    examples and open source apps *do not* use the ViewHolder to avoid trips
    to findViewById. If your list is small, this might not be a big deal,
    but if the list is large it can make a difference in terms of frame
    rate, resource usage, and performance (which equals better experience
    for the user, longer battery life, etc.). Also, it's a good habit to be
    in even with small lists, more efficient code is better, especially on a
    mobile platform.

    So how do you do this stuff? Well, here is the Adapter from the aforementioned DevNexus 2010 application to demonstrate:

    01 static class ViewHolder {
    02    private TextView view1;
    03    private TextView view2;
    04 }
    05  
    06 private class PresentationAdapter extends ArrayAdapter<Presentation&g<wbr>t; {
    07  
    08    LayoutInflater vi = (LayoutInflater) PresentationList.this.getSyste<wbr>mService(Context.LAYOUT_INFLAT<wbr>ER_SERVICE);
    09    private final ArrayList<Presentation> presentations;
    10  
    11    public PresentationAdapter(final Context context, final int resId, final ArrayList<Presentation> presentations) {
    12       super(context, resId, presentations);
    13       this.presentations = presentations;
    14    }
    15  
    16    @Override
    17    public View getView(final int position, final View convertView, final ViewGroup parent) {
    18       // re-use the convertView, try not to recreate objects here or inflate every time (expensive)
    19       // also use the "tag" with the "view holder" pattern to avoid findViewById every time
    20       View v = convertView;
    21       if (v == null) {
    22          v = vi.inflate(R.layout.list_items<wbr>_item, null);
    23          ViewHolder viewHolder = new ViewHolder();
    24          viewHolder.view1 = (TextView) v.findViewById(R.id.list_item_<wbr>above);
    25          viewHolder.view2 = (TextView) v.findViewById(R.id.list_item_<wbr>below);
    26          v.setTag(viewHolder);
    27       }
    28  
    29       Presentation p = presentations.get(position);
    30       if (p != null) {
    31          ViewHolder viewHolder = (ViewHolder) v.getTag();
    32          viewHolder.view1.setText(p.nam<wbr>e);
    33          viewHolder.view2.setText(p.spe<wbr>aker);           
    34       }
    35       return v;
    36    }
    37 }

    The complete code for the DevNexus application (for context), is here.
    What we are focusing on above is the Adapter to the ListView. Within it
    we see that we are making use of the convertView if present to grab our
    subsequent views, and to populate our own internal ViewHolder
    representation. If the convertView isn't present, then we do the extra
    work of inflating our views and building our ViewHolder.

    The "convertView" is a convenient helper object provided to you by
    the framework specifically for re-use (if the framework is ready to
    provide it, the first item in the list will have a null convertView,
    hence the check, and the work only if it is null). The framework knows
    the views that are passing by, and as it recycles them it hands you one
    that you can "convert."

    So what is the ViewHolder? Well, that's really just a simple object
    we have created (using a package scoped inner class) that stores child
    views (though you could store anything there, that is all we need for
    this example). View objects have a "Tag" property that you can use to
    store any object. If you are getting recycled views anyway, it's very
    easy and helpful to just stick child views you need *there* and retrieve
    later, as opposed to calling "findViewById" to pull views from
    resources for every view in the list. Really this just saves us the
    findViewById calls. That might not seem like much, but again, in large
    lists it can cut down on overhead.

    A little further down in the code, after we get past creating the
    convertView in the case that it's null, we see where we use the
    ViewHolder to get a reference to our subsequent child views, and set
    stuff (in this case just Strings, but could be anything).

    This is a simple example, and I certainly won't promise that it's
    perfect, but hopefully it does help to explain what ViewHolder is, and
    why it's useful (and a little about re-cycling views in general).

    Comments Alternative to the ViewHolder Submitted by Uncle Code Monkey (not verified) on Tue, 12/21/2010 - 10:54.

    Thanks for writing on this subject. In the past, I did not bother
    with caching the find results and after reading this blog, I decided to
    give it a try and see if it improves the speed of my app any. I noticed
    my app become more responsive and that's all the convincing I need to
    keep it.

    I do, however, have a different approach to caching than how you
    wrote this article. Instead of keeping a separate class in which to
    store the result of the various finds, I just store it directly into the
    View.tag mechanism like so:

    1 protected Object getViewHandle(View aParentView, int aViewToFind) {
    2     Object v = aParentView.getTag(aViewToFind<wbr>);
    3     if (v==null) {
    4         v = aParentView.findViewById(aView<wbr>ToFind);
    5         aParentView.setTag(aViewToFind<wbr>,v);        
    6     }
    7     return v;
    8 }

    and then call it in my code by

    1 TextView tv = (TextView)getViewHandle(itemVi<wbr>ew,R.id.some_text_view);

    This way I don't need to define classes for each kind of item view in
    the list and makes the technique easily adaptable for different lists
    without introducing another class that needs to be passed around to all
    the code that works with such views.

    Thanks for the inspiration!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.