1page.title=ListView Backgrounds: An Optimization 2parent.title=Articles 3parent.link=../browser.html?tag=article 4@jd:body 5 6<p>{@link android.widget.ListView} is one of Android's most widely used widgets. 7It 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 11to use a custom background. By default, like many Android widgets, 12<code>ListView</code> has a transparent background which means that you can see 13through 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 16see at the top of the following screenshot — the first text item gradually 17fades to black. This technique is used throughout the system to indicate that 18the 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()} 24and 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 27background on the <code>ListView</code> or when you change the window's 28background. The following two screenshots show what happens in an application 29when you change the window's background. The left image shows what the list 30looks like by default and the right image shows what the list looks like during 31a 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 38enabled by default on all instances of <code>ListView</code>. I mentioned 39earlier that the fade effect is implemented using a Porter-Duff blending mode. 40This implementation works really well but is unfortunately very costly and can 41bring down drawing performance by quite a bit as it requires to capture a 42portion 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 46background, there is no reason to go down that expensive route. That's why we 47introduced an optimization called the "cache color hint." The cache color hint 48is an RGB color set by default to the window's background color, that is #191919 49in Android's dark theme. When this hint is set, <code>ListView</code> (actually, 50its base class <code>View</code>) knows it will draw on a solid background and 51therefore replaces th expensive <code>saveLayerAlpha()/Porter-Duff</code> 52rendering with a simple gradient. This gradient goes from fully transparent to 53the cache color hint value and this is exactly what you see on the image above, 54with the dark gradient at the bottom of the list. However, this still does not 55explain why the entire list turns black during a scroll.</p> 56 57<p>As mentioned before, <code>ListView</code> has a transparent/translucent 58background by default, and so all default widgets in the Android UI toolkit. 59This implies that when <code>ListView</code> redraws its children, it has to 60blend the children with the window's background. Once again, this requires 61costly readbacks from memory that are particularly painful during a scroll or a 62fling when drawing happens dozen of times per second. </p> 63 64<p>To improve drawing performance during scrolling operations, the Android 65framework reuses the cache color hint. When this hint is set, the framework 66copies each child of the list in a <code>Bitmap</code> filled with the hint 67value (assuming that another optimization, called <em>scrolling cache</em>, is 68not turned off). <code>ListView</code> then blits these bitmaps directly on 69screen and because these bitmaps are known to be opaque, no blending is 70required. Also, since the default cache color hint is <code>#191919</code>, you 71get 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 74optimization, if you use a non-solid color background, or set the hint to the 75appropriate solid color value. You can do this from code (see 76{@link android.widget.AbsListView#setCacheColorHint(int)}) or preferably from 77XML, by using the <code>android:cacheColorHint</code> attribute. To disable the 78optimization, simply use the transparent color <code>#00000000</code>. The 79following 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 85background. The cache color hint feature is interesting because it 86shows how optimizations can make your life more difficult in 87some situations. In this particular case, however, the benefit of the 88default behavior outweighs the added complexity..</p> 89