diff options
Diffstat (limited to 'docs/html/resources/articles/listview-backgrounds.jd')
-rw-r--r-- | docs/html/resources/articles/listview-backgrounds.jd | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/docs/html/resources/articles/listview-backgrounds.jd b/docs/html/resources/articles/listview-backgrounds.jd new file mode 100644 index 0000000..f4c6998 --- /dev/null +++ b/docs/html/resources/articles/listview-backgrounds.jd @@ -0,0 +1,86 @@ +page.title=ListView Backgrounds: An Optimization +@jd:body + +<p>{@link android.widget.ListView} is one of Android's most widely used widgets. +It is rather easy to use, very flexible, and incredibly powerful. +<code>ListView</code> can also be difficult to understand at times.</p> + +<p>One of the most common issues with <code>ListView</code> happens when you try +to use a custom background. By default, like many Android widgets, +<code>ListView</code> has a transparent background which means that you can see +through the default window's background, a very dark gray +(<code>#FF191919</code> with the current <code>dark</code> theme.) Additionally, +<code>ListView</code> enables the <em>fading edges</em> by default, as you can +see at the top of the following screenshot — the first text item gradually +fades to black. This technique is used throughout the system to indicate that +the container can be scrolled.</p> + +<div style="text-align: center;"><img src="images/list_fade_1.png" alt="Android's default ListView"></div> + +<p>The fade effect is implemented using a combination of +{@link android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int) Canvas.saveLayerAlpha()} +and the {@link android.graphics.PorterDuff.Mode#DST_OUT Porter-Duff Destination Out blending mode}. </p> + +<p>Unfortunately, things start to get ugly when you try to use a custom +background on the <code>ListView</code> or when you change the window's +background. The following two screenshots show what happens in an application +when you change the window's background. The left image shows what the list +looks like by default and the right image shows what the list looks like during +a scroll initiated with a touch gesture:</p> + +<div style="text-align: center;"> +<img style="margin-right: 12px;" src="images/list_fade_2.png" alt="Dark fade"> +<img src="images/list_fade_3.png" alt="Dark list"></div> + +<p>This rendering issue is caused by an optimization of the Android framework +enabled by default on all instances of <code>ListView</code>. I mentioned +earlier that the fade effect is implemented using a Porter-Duff blending mode. +This implementation works really well but is unfortunately very costly and can +bring down drawing performance by quite a bit as it requires to capture a +portion of the rendering in an offscreen bitmap and then requires extra blending +(which implies readbacks from memory.)</p> + +<p>Since <code>ListView</code> is most of the time displayed on a solid +background, there is no reason to go down that expensive route. That's why we +introduced an optimization called the "cache color hint." The cache color hint +is an RGB color set by default to the window's background color, that is #191919 +in Android's dark theme. When this hint is set, <code>ListView</code> (actually, +its base class <code>View</code>) knows it will draw on a solid background and +therefore replaces th expensive <code>saveLayerAlpha()/Porter-Duff</code> +rendering with a simple gradient. This gradient goes from fully transparent to +the cache color hint value and this is exactly what you see on the image above, +with the dark gradient at the bottom of the list. However, this still does not +explain why the entire list turns black during a scroll.</p> + +<p>As mentioned before, <code>ListView</code> has a transparent/translucent +background by default, and so all default widgets in the Android UI toolkit. +This implies that when <code>ListView</code> redraws its children, it has to +blend the children with the window's background. Once again, this requires +costly readbacks from memory that are particularly painful during a scroll or a +fling when drawing happens dozen of times per second. </p> + +<p>To improve drawing performance during scrolling operations, the Android +framework reuses the cache color hint. When this hint is set, the framework +copies each child of the list in a <code>Bitmap</code> filled with the hint +value (assuming that another optimization, called <em>scrolling cache</em>, is +not turned off). <code>ListView</code> then blits these bitmaps directly on +screen and because these bitmaps are known to be opaque, no blending is +required. Also, since the default cache color hint is <code>#191919</code>, you +get a dark background behind each item during a scroll.</p> + +<p>To fix this issue, all you have to do is either disable the cache color hint +optimization, if you use a non-solid color background, or set the hint to the +appropriate solid color value. You can do this from code (see +{@link android.widget.AbsListView#setCacheColorHint(int)}) or preferably from +XML, by using the <code>android:cacheColorHint</code> attribute. To disable the +optimization, simply use the transparent color <code>#00000000</code>. The +following screenshot shows a list with +<code>android:cacheColorHint="#00000000"</code> set in the XML layout file:</p> + +<div style="text-align: center;"><img src="images/list_fade_4.png" alt="Fade on a custom background"></div> + +<p>As you can see, the fade works perfectly against the custom wooden +background. The cache color hint feature is interesting because it +shows how optimizations can make your life more difficult in +some situations. In this particular case, however, the benefit of the +default behavior outweighs the added complexity..</p> |