Home | History | Annotate | Download | only in articles
      1 page.title=ListView Backgrounds: An Optimization
      2 parent.title=Articles
      3 parent.link=../browser.html?tag=article
      4 @jd:body
      5 
      6 <p>{@link android.widget.ListView} is one of Android's most widely used widgets.
      7 It is rather easy to use, very flexible, and incredibly powerful. 
      8 <code>ListView</code> can also be difficult to understand at times.</p>
      9 
     10 <p>One of the most common issues with <code>ListView</code> happens when you try
     11 to use a custom background. By default, like many Android widgets,
     12 <code>ListView</code> has a transparent background which means that you can see
     13 through the default window's background, a very dark gray
     14 (<code>#FF191919</code> with the current <code>dark</code> theme.) Additionally,
     15 <code>ListView</code> enables the <em>fading edges</em> by default, as you can
     16 see at the top of the following screenshot &mdash; the first text item gradually
     17 fades to black. This technique is used throughout the system to indicate that
     18 the container can be scrolled.</p>
     19 
     20 <div style="text-align: center;"><img src="images/list_fade_1.png" alt="Android's default ListView"></div>
     21 
     22 <p>The fade effect is implemented using a combination of 
     23 {@link android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int) Canvas.saveLayerAlpha()} 
     24 and the {@link android.graphics.PorterDuff.Mode#DST_OUT Porter-Duff Destination Out blending mode}. </p>
     25 
     26 <p>Unfortunately, things start to get ugly when you try to use a custom
     27 background on the <code>ListView</code> or when you change the window's
     28 background. The following two screenshots show what happens in an application
     29 when you change the window's background. The left image shows what the list
     30 looks like by default and the right image shows what the list looks like during
     31 a scroll initiated with a touch gesture:</p>
     32 
     33 <div style="text-align: center;">
     34 <img style="margin-right: 12px;" src="images/list_fade_2.png" alt="Dark fade">
     35 <img src="images/list_fade_3.png" alt="Dark list"></div>
     36 
     37 <p>This rendering issue is caused by an optimization of the Android framework
     38 enabled by default on all instances of <code>ListView</code>. I mentioned
     39 earlier that the fade effect is implemented using a Porter-Duff blending mode.
     40 This implementation works really well but is unfortunately very costly and can
     41 bring down drawing performance by quite a bit as it requires to capture a
     42 portion of the rendering in an offscreen bitmap and then requires extra blending
     43 (which implies readbacks from memory.)</p>
     44 
     45 <p>Since <code>ListView</code> is most of the time displayed on a solid
     46 background, there is no reason to go down that expensive route. That's why we
     47 introduced an optimization called the "cache color hint." The cache color hint
     48 is an RGB color set by default to the window's background color, that is #191919
     49 in Android's dark theme. When this hint is set, <code>ListView</code> (actually,
     50 its base class <code>View</code>) knows it will draw on a solid background and
     51 therefore replaces th expensive <code>saveLayerAlpha()/Porter-Duff</code>
     52 rendering with a simple gradient. This gradient goes from fully transparent to
     53 the cache color hint value and this is exactly what you see on the image above,
     54 with the dark gradient at the bottom of the list. However, this still does not
     55 explain why the entire list turns black during a scroll.</p>
     56 
     57 <p>As mentioned before, <code>ListView</code> has a transparent/translucent
     58 background by default, and so all default widgets in the Android UI toolkit.
     59 This implies that when <code>ListView</code> redraws its children, it has to
     60 blend the children with the window's background. Once again, this requires
     61 costly readbacks from memory that are particularly painful during a scroll or a
     62 fling when drawing happens dozen of times per second. </p>
     63 
     64 <p>To improve drawing performance during scrolling operations, the Android
     65 framework reuses the cache color hint. When this hint is set, the framework
     66 copies each child of the list in a <code>Bitmap</code> filled with the hint
     67 value (assuming that another optimization, called <em>scrolling cache</em>, is
     68 not turned off). <code>ListView</code> then blits these bitmaps directly on
     69 screen and because these bitmaps are known to be opaque, no blending is
     70 required. Also, since the default cache color hint is <code>#191919</code>, you
     71 get a dark background behind each item during a scroll.</p>
     72 
     73 <p>To fix this issue, all you have to do is either disable the cache color hint
     74 optimization, if you use a non-solid color background, or set the hint to the
     75 appropriate solid color value. You can do this from code (see
     76 {@link android.widget.AbsListView#setCacheColorHint(int)}) or preferably from
     77 XML, by using the <code>android:cacheColorHint</code> attribute. To disable the
     78 optimization, simply use the transparent color <code>#00000000</code>. The
     79 following screenshot shows a list with
     80 <code>android:cacheColorHint="#00000000"</code> set in the XML layout file:</p>
     81 
     82 <div style="text-align: center;"><img src="images/list_fade_4.png" alt="Fade on a custom background"></div>
     83 
     84 <p>As you can see, the fade works perfectly against the custom wooden
     85 background. The cache color hint feature is interesting because it
     86 shows how optimizations can make your life more difficult in
     87 some situations. In this particular case, however, the benefit of the
     88 default behavior outweighs the added complexity..</p>
     89