Home | History | Annotate | Download | only in improving-layouts
      1 page.title=Making ListView Scrolling Smooth
      2 parent.title=Improving Layout Performance
      3 parent.link=index.html
      4 
      5 trainingnavtop=true
      6 previous.title=Loading Views On Demand
      7 previous.link=loading-ondemand.html
      8 
      9 @jd:body
     10 
     11 
     12 <div id="tb-wrapper">
     13 <div id="tb">
     14 
     15 <!-- table of contents -->
     16 <h2>This lesson teaches you to</h2>
     17 <ol>
     18   <li><a href="#AsyncTask">Use a Background Thread</a></li>
     19   <li><a href="#ViewHolder">Hold View Objects in a View Holder</a></li>
     20 </ol>
     21 
     22 <!-- other docs (NOT javadocs) -->
     23 <h2>You should also read</h2>
     24 <ul>
     25   <li><a href="http://android-developers.blogspot.com/2009/01/why-is-my-list-black-android.html">Why
     26       is my list black? An Android optimization</a></li>
     27 </ul>
     28 
     29 </div>
     30 </div>
     31 
     32 <p>The key to a smoothly scrolling {@link android.widget.ListView} is to keep the applications main
     33 thread (the UI thread) free from heavy processing. Ensure you do any disk access, network access, or
     34 SQL access in a separate thread. To test the status of your app, you can enable {@link
     35 android.os.StrictMode}.</p>
     36 
     37 
     38 <h2 id="AsyncTask">Use a Background Thread</h2>
     39 
     40 <p>Using a background thread ("worker thread") removes strain from the main thread so it can focus
     41 on drawing the UI. In many cases, using {@link android.os.AsyncTask} provides a simple way to
     42 perform your work outside the main thread. {@link android.os.AsyncTask} automatically queues up all
     43 the {@link android.os.AsyncTask#execute execute()} requests and performs them serially. This
     44 behavior is global to a particular process and means you dont need to worry about creating your
     45 own thread pool.</p>
     46 
     47 <p>In the sample code below, an {@link android.os.AsyncTask} is used to load
     48 images in a background thread, then apply them to the UI once finished. It also shows a
     49 progress spinner in place of the images while they are loading.</p>
     50 
     51 <pre>
     52 // Using an AsyncTask to load the slow images in a background thread
     53 new AsyncTask&lt;ViewHolder, Void, Bitmap>() {
     54     private ViewHolder v;
     55 
     56     &#64;Override
     57     protected Bitmap doInBackground(ViewHolder... params) {
     58         v = params[0];
     59         return mFakeImageLoader.getImage();
     60     }
     61 
     62     &#64;Override
     63     protected void onPostExecute(Bitmap result) {
     64         super.onPostExecute(result);
     65         if (v.position == position) {
     66             // If this item hasn't been recycled already, hide the
     67             // progress and set and show the image
     68             v.progress.setVisibility(View.GONE);
     69             v.icon.setVisibility(View.VISIBLE);
     70             v.icon.setImageBitmap(result);
     71         }
     72     }
     73 }.execute(holder);
     74 </pre>
     75 
     76 <p>Beginning with Android 3.0 (API level 11), an extra feature is available in {@link
     77 android.os.AsyncTask} so you can enable it to run across multiple processor cores. Instead of
     78 calling {@link android.os.AsyncTask#execute execute()} you can specify {@link
     79 android.os.AsyncTask#executeOnExecutor executeOnExecutor()} and multiple requests can be executed at
     80 the same time depending on the number of cores available.</p>
     81 
     82 
     83 <h2 id="ViewHolder">Hold View Objects in a View Holder</h2>
     84 
     85 <p>Your code might call {@link android.app.Activity#findViewById findViewById()} frequently
     86 during the scrolling of {@link android.widget.ListView}, which can slow down performance. Even when
     87 the {@link
     88 android.widget.Adapter} returns an inflated view for recycling, you still need to look up the
     89 elements
     90 and update them. A way around repeated use of {@link android.app.Activity#findViewById
     91 findViewById()} is to use the "view holder" design pattern.</p>
     92 
     93 <p>A {@code ViewHolder} object stores each of the component views inside the tag field of the
     94 Layout, so you can immediately access them without the need to look them up repeatedly. First, you
     95 need to create a class to hold your exact set of views. For example:</p>
     96 
     97 <pre>
     98 static class ViewHolder {
     99   TextView text;
    100   TextView timestamp;
    101   ImageView icon;
    102   ProgressBar progress;
    103   int position;
    104 }
    105 </pre>
    106 
    107 <p>Then populate the {@code ViewHolder} and store it inside the layout.</p>
    108 
    109 <pre>
    110 ViewHolder holder = new ViewHolder();
    111 holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
    112 holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
    113 holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
    114 holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
    115 convertView.setTag(holder);
    116 </pre>
    117 
    118 <p>Now you can easily access each view without the need for the look-up, saving valuable processor
    119 cycles.</p>
    120 
    121 
    122 
    123 
    124 
    125