I just spent the weekend rewriting an Android application for performance.
When I first start learning a UI framework, be it for iOS, Android, Java Swing, GWT, MacOS, Windows, or X, the two questions I first want to answer are:
- How do I build a custom view?
and
- How do I build a custom view container and perform custom layout of the children within that container?
With those two bits of information you can rule the world. (Or at least the framework.)
The problem with most applications running like a dog, especially on mobile devices, is that most frameworks are inefficient at maintaining more than a couple of dozen views within a window. This isn’t a problem when you’re talking about putting up a dialog with a bunch of controls or have a relatively static display. But when you start talking about dragging and dropping objects, or when you are talking about scrolling items in a scroll view, things can go to hell very quickly.
To take a concrete example, I put together a view which contains a scrolling area, and inside the area each item in the list of items is represented by an image, a couple of buttons and a label of text. The natural way in Android to do this is to build a ListView, create a ListAdapter and in response to each request for a view, use a LayoutInflater (as needed) to construct a view hierarchy that contains a layout or three, representing the buttons as views, the image as a view, and the text as a view, all layered on other views. On the iPhone it’s the same story; a UITableViewCell can contain a hierarchy of other views which represent the contents of the cell.
For a list of 20 items, this translates into over a hundred-something views, minimum.
And on both Android and iOS, dragging around all that crap takes forever.
My solution in each of these cases is to reduce the complexity. On the iPhone override the UITableViewCell as a single custom view which draws the buttons, widgets and components in the -drawView() method. That way, on the screen you have 7 views, not over a hundred.
On Android the solution was even more radical: instead of a list view, I just used a ScrollView and created a custom view which draws the entire list. Use the Canvas’ getClipBounds() method in the canvas passed into onDraw() to determine what needs to be drawn, and draw it all in one view.
With this technique you eliminate manipulating a hundred views, and can easily make something go from impossibly jerky to smooth as silk, even on slower devices.