• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Making ListView Scrolling Smooth
2parent.title=Improving Layout Performance
3parent.link=index.html
4
5trainingnavtop=true
6previous.title=Loading Views On Demand
7previous.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="{@docRoot}resources/articles/listview-backgrounds.html">ListView
26Backgrounds: An 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 application’s main
33thread (the UI thread) free from heavy processing. Ensure you do any disk access, network access, or
34SQL access in a separate thread. To test the status of your app, you can enable {@link
35android.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
41on drawing the UI. In many cases, using {@link android.os.AsyncTask} provides a simple way to
42perform your work outside the main thread. {@link android.os.AsyncTask} automatically queues up all
43the {@link android.os.AsyncTask#execute execute()} requests and performs them serially. This
44behavior is global to a particular process and means you don’t need to worry about creating your
45own thread pool.</p>
46
47<p>In the sample code below, an {@link android.os.AsyncTask} is used to load
48images in a background thread, then apply them to the UI once finished. It also shows a
49progress 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
53new 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
77android.os.AsyncTask} so you can enable it to run across multiple processor cores. Instead of
78calling {@link android.os.AsyncTask#execute execute()} you can specify {@link
79android.os.AsyncTask#executeOnExecutor executeOnExecutor()} and multiple requests can be executed at
80the 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
86during the scrolling of {@link android.widget.ListView}, which can slow down performance. Even when
87the {@link
88android.widget.Adapter} returns an inflated view for recycling, you still need to look up the
89elements
90and update them. A way around repeated use of {@link android.app.Activity#findViewById
91findViewById()} 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
94Layout, so you can immediately access them without the need to look them up repeatedly. First, you
95need to create a class to hold your exact set of views. For example:</p>
96
97<pre>
98static 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>
110ViewHolder holder = new ViewHolder();
111holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
112holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
113holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
114holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
115convertView.setTag(holder);
116</pre>
117
118<p>Now you can easily access each view without the need for the look-up, saving valuable processor
119cycles.</p>
120
121
122
123
124
125