Android Protip to make lists with dynamically loaded images butter smooth

Assume you have list with items, each item containing an image which is dynamically loaded from a server. A placeholder is shown if the image hasn’t been loaded yet, and as soon as it is loaded, it gets faded in.

Technically speaking, ImageView.setImageDrawable gets called with the loaded image (which hopefully is loaded and decoded in a thread with lower priority than the main thread) as soon as the image is ready. So what could be a problem with that?

If we take a look at the source code (https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ImageView.java#L390), we see that if the drawable doesn’t have the same intrinsic bounds (basically the width and height of the bitmap), a layout pass gets scheduled with the requestLayout() call. In general, this is necessary since the image view might adjust its bounds depending on the content, pushing other elements around in the view hierarchy. However, in most lists with images dynamically loaded, the layout will be exactly the same. It would also confuse the user if elements would move around.

You might ask, does this layout pass really make a difference? Have a look at the screenshot from systrace (if you don’t know about systrace, head over to Google I/O 2012 - For Butter or Worse: Smoothing Out Performance in Android UIs or http://developer.android.com/tools/debugging/systrace.html - it’s the most useful tool for jankbusting) below. You see that everything is evenly distributed and performTraversals is way below the magical 16ms, except in the middle, where we have an unnecessary layout pass caused by what I explained above. Does it matter? Yes it does. We miss the magical 16ms, causing that a frame gets “dropped”. People will notice this - they won’t necessarily understand what’s going on, but they will feel it.

So what can we do to fix this? As mentioned above, there is nothing the framework can do for that - the image view doesn’t know how it gets laid out from the parent, and whenever the bounds change its drawable, views might get laid out differently. However, if you are completely sure that the layout will stay the same when you change the drawable, you can block that layout request by subclassing ImageView: http://pastie.org/5348627, where mIsFixedSize must be true if and only if you are sure that the layout will stay the same. For example, if the image view has set its layout_width and layout_height to an explicit dp value and the parent respects that value (be careful with linear layouts!), mIsFixedSize could be set to true. Note that you should only block the layout if you know what you are doing. If a layout pass is really necessary, and you are blocking it, then strange bugs and artifacts can occur.
Shared publiclyView activity