2015-01-27 12:00:00
Improvements in Xamarin.Forms 1.3
Back in November I wrote a blog entry about performance problems resulting from the design of the layout system in Xamarin.Forms. I am pleased to report that things took a big step forward with the recent release of version 1.3.
Reviewing the problem
In a nutshell, the Layout classes do too much. They contain functionality to make sure everything gets updated whenever something changes. In principle, this is good, since we obviously don't want stale stuff on the screen. But in practice, there are many cases where the built-in update code ends up being slower than necessary.
For example, suppose I'm going to add ten child views to a layout. With the built-in update code, a layout cycle will get triggered ten times, once for each child view I add. Worse, if I'm trying to do any kind of subview recycling, the odds are high that I want to add a child view while I am processing a layout cycle. This will trigger a recursive layout cycle, resulting in the end of civilization as we know it.
Instead, what I want is one layout cycle which happens after all ten child views have been added.
The solution I proposed
IMHO, the best design for this kind of problem is to have multiple layers:
The Low-Level layer models child view relationships only. It provides a way for a View to be inside another View, but it doesn't give much more than that. In iOS terms, this is UIView.addSubView.
The High-Level layer (which is built on the functionality provided by the layers below it) has Views which actively manage their child views. In iOS terms, an example of this would be UICollectionView.
In the Middle, it would make sense to have a layer which provides things which are common to all (or nearly all) of the stuff in the High-Level layer, to avoid code duplication.
Xamarin.Forms has the High-Level layer and the Middle layer, but it does not have the Low-Level layer. So I proposed creating it.
I didn't get exactly what I wanted, but...
The solution in Xamarin.Forms 1.3
In Xamarin.Forms 1.3, the Middle layer is still the lowest thing we've got. However, there are new capabilities which allow the Middle layer to pretend like it is a Low-Level layer. It still has a bunch of built-in update code, but now that code can be turned off. :-)
The important new capabilities are:
- ShouldInvalidateOnChildAdded
- ShouldInvalidateOnChildRemoved
- OnChildMeasureInvalidated
By returning false from my override of ShouldInvalidateOnChildAdded() and ShouldInvalidateOnChildRemoved(), I can have a Layout which doesn't do any automatic updates when I add or remove children.
And by overriding OnChildMeasureInvalidated(), I can have a Layout which refuses to do real estate negotiations with its child views.
This is good.
How I'm using this
Because of this new stuff, an upcoming release of our DataGrid component will be even faster. Our panel layout class will look something like this:
private class myLayout : Layout<View> { Func<View,Rectangle> getBox; public myLayout(Func<View,Rectangle> f) { getBox = f; } public void LayoutOneChild(View v) { Rectangle r = getBox (v); v.Layout (r); } public void LayoutAllChildren() { foreach (View v in Children) { LayoutOneChild (v); } } protected override bool ShouldInvalidateOnChildAdded (View child) { return false; // stop pestering me } protected override bool ShouldInvalidateOnChildRemoved (View child) { return false; // go away and leave me alone } protected override void OnChildMeasureInvalidated () { // I'm ignoring you. You'll take whatever size I want to give // you. And you'll like it. } protected override void LayoutChildren (double x, double y, double width, double height) { LayoutAllChildren (); } }
This Layout class is obviously very simplistic, but it merely scratches the surface of what becomes possible now that Xamarin.Forms has [something that can imitate] a Low-Level subview layer.
Kudos and thanks to the Xamarin.Forms team!