Why is my list black? An Android optimization

ListView is one of Android’s most widely used widgets. It is rather easy to user, very flexible and incredibly powerful. ListView can also be difficult to understand at times.

One of the most common issues with ListView happens when you try to use a custom background. By default, like many Android widgets, ListView has a transparent background which means yo can see through the default window’s background, a very dark gray (#FF191919 with the current dark theme.) Additionally, ListView enables the fading edges 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.

Android's default ListView

The fade effect is implemented using a combination of Canvas.saveLayerAlpha() and the Porter-Duff Destination Out blending mode. This technique is similar to the one explained in Filthy Rich Clients and various presentations. Unfortunately, things start to get ugly when you try to use a custom background on the ListView 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:

Dark fadeDark list

This rendering issue is caused by an optimization of the Android framework enabled by default on all instances of ListView (for some reason, I forgot to enable it by default on GridView.) 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.)

Since ListView 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, ListView (actually, its base class View) knows it will draw on a solid background and therefore replaces th expensive saveLayerAlpha()/Porter-Duff 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.

As I said before, ListView has a transparent/translucent background by default, and so all default Android widgets. This implies that when ListView 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. 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 Bitmap filled with the hint value (this assumes that another optimization, called scrolling cache, is not turned off.) ListView then blits these bitmaps directly on screen and because these bitmaps are known to be opaque, no blending is required. And since the default cache color hint is #191919, you get a dark background behind each item during a scroll.

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. This can be dome from code or preferably from XML, by using the android:cacheColorHint attribute. To disable the optimization, simply use the transparent color #00000000. The following screenshot shows a list with android:cacheColorHint="#00000000" set in the XML layout file:

Fade on a custom background

As you can see, the fade works perfectly against the custom wooden background. I find the cache color hint feature interesting because it shows how optimizations can make developers’ life more difficult in some situations. In this particular case, however, the benefit of the default behavior outweighs the added complexity for the developer.

17 Responses to “Why is my list black? An Android optimization”

  1. dak says:

    Why don’t just disable the cache color hint optimization when someone sets a background image, and update the cache color hint when someone sets the background color? I’m probably ignorant, but I can’t think of any cases where you would want to manually set the cache color hint outside of those two cases, and I can’t think of any cases where you would want the default behavior after setting the background image/color.

  2. Romain Guy says:

    A background image is a drawable, ListView doesn’t really have a way to know that it’s an image without doing some ugly things. The cache color hint could be updated when a solid color is set *on the ListView.* It won’t help in the other case, when the custom background is behind the ListView.

  3. dak says:

    OK, the situation is more complicated than I originally understood. However, I still balk at optimizations that force users (in this case, app developers) to deal with their side effects. I also worry that these kinds of details make reuse more difficult by making the view’s properties dependent on external state in ways that aren’t immediately clear.

    To hide this optimization, the hurdle we need to overcome is that the ListView knows about the cache color hint but doesn’t have the information it needs to determine the correct value, while the parent has the information to set the correct value but doesn’t (and shouldn’t) know about the cache color hint. Put this way, the solution that comes immediately to mind is event-based: Let the children listen for changes to parent properties they’re interested in. This way, the ListView can observe changes to the window background, get the updated value, and update its cache color hint as appropriate.

  4. Romain Guy says:

    Like I said, we are aware that such an optimization is an extra burden for the developer but we are also convinced that the benefits outweighs the added complexity. As for using events to implement, this would add extra complexity to the framework and more dispatching code that would not be leveraged in most situations. So no, it won’t happen (at least not for now.)

  5. slegge says:

    This may sound like a silly comment, but I don’t much care about hand held devices, but I do enjoy swing. Some of these android screen shots look cool, is there any chance I can use these cool looking android widgets in a regular desktop/applet swing application? If so, perhaps we might see some cool WebStart examples?

  6. Romain Guy says:

    Unfortunately no. The Eclipse plugin renders the Android widgets with Java2D but they are not interactive. I guess it would be possible to port the Android widget to a desktop, but it would probably require a lot of work and would not end up being that great since the widgets were not designed, at least with the current theme, to be used with a mouse.

  7. vovkab says:

    Here is Translation of article to russian: http://androidteam.ru/dev/particles/why-my-listview-black.html

    Can you add link?

  8. Mike Hearn says:

    Could the ListView do something like:

    if (getBackgroundDrawable() instanceof ColorDrawable)
    // optimization on
    else
    // optimization off

    I know that nothing stops people creating their own drawables that do the same thing as ColorDrawable, but it seems that this would 99% of the time mean developers don’t have to think about this. Even if the ugly things are a lot uglier than this, it’d still be *really* great to not have this kind of sharp corner in the API. Writing Android apps is already pretty complicated!

  9. Romain Guy says:

    Your solution doesn’t account for the most common case: a transparent ListView on top of a window with a solid colored background. We thought about a similar solution that would take into account the color and opacity of the ancestors of ListView but we decided that the extra complexity (maintenance, performance, etc.) was not worth it.

  10. Mike Hearn says:

    Sure OK, but my point is, a solution that gets it right 80% of the time is still worth doing, because it’ll save 80% of developers from having to find this blog post and figure out what’s going on. Even if you don’t get every case and even make the remaining 20% of cases more obscure, it’d still help people in the majority case.

    I suppose at some point the hardware will get powerful enough that this optimization isn’t necessary anymore. The patterned backgrounds in earlier SDKs were nice, it’s a shame they disappeared (i guess this is why).

  11. Romain Guy says:

    It’s still not worth doing it because 80% of the time, people do not change the background of the ListView.

    And the patterned background disappeared because it added too much visual clutter.

  12. Matthias says:

    Hi,

    I found styling lists in Android to behave very strange. As soon as I declare a custom List style in my theme, the list divider will disappear in the rendered list, even if I inherit all defaults from @android:attr/listViewStyle!

    Setting a custom divider also has never worked for me whatsoever…

    I can only make it appear again when removing the line

    ?style/MyListView

    from my theme declaration.

    Is this a bug?

  13. Matthias says:

    whoops, it ate all the markup I posted… well, you get the idea.

  14. moskoviaa says:

    Добрый день
    Это мое сообщение к администрации – http://www.curious-creature.org,
    Обменяюсь баннерами с вашим сайтом , хочу чтобы они были в этот или можем обсудить другие варианты сотрудничества.
    Наш сайт forum-grad.ru
    На этот форум о Подмосковье заходят более 3000 человек, сайт развиваеться проводяться конкурсы, каждый день разыгриваються призы “100р на телефон” и мы готовы к сотрудничеству.
    контакты info(собака)forum-grad.ru
    p/s
    прошу извинить за вторжени

  15. Rajeev says:

    Please tell how to conver the text color of a ListVeiw in android.