Build Fast and Beautiful Apps with Xamarin.Forms NET323 Michael Ridland
Who’s this guy? Michael Ridland michaelridland.com / xam-consulting.com @rid00z michael@xam-consulting.com
Agenda Misconceptions & Truths Styling Features Performance Tips Layout System Pages, Layouts and Controls Animations
Misconceptions & Truths
Xamarin.Forms is Slow? That’s what reddit said Past experiences (current release 5-10x faster than 1.0) It’s Slow and Fast Xamarin.Forms is easy Easy can be deceiving Your still on a phone with limited resources… Forms Xaml is not like WPF (it doesn’t forgive) With Forms you must build pages with Performance in mind… Xamarin.Forms is Fast but NOT forgiving
What’s Fast? Pages with less elements Post-Layout Animations/Translations Leveraging performance features (XamlC, ListViews, Grids, Recycling) Following the Xamarin performance recommendations Leveraging 3rd Party Tools like FFImageLoader
What’s Slow? Overloaded pages with deep nesting ** Startup Time - 2-3 seconds for a app vs <1 sec Causing too many layout cycles Dynamically building UI elements Ignoring the Xamarin performance recommendations
How do I build Fast and Beautiful Apps with Xamarin.Forms?
Beautiful - is not always about the platform Visual Design UX Design
Fast - is not always about the platform Caching Data Caching View Caching Imaging Caching Background Downloading Begin Downloading in Background Show Your View Download Completed Populate Missing Parts Sequence
Styling in Xamarin.Forms
Styling in Xamarin.Forms AppResources Custom Fonts Custom Renderers Effects Different Screen Sizes
App Resources - Control Properties and Styles <?xml version="1.0" encoding="utf-8"?> <Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="FBPlay.App"> <Application.Resources> <ResourceDictionary> <Style x:Key="HeadingStyle" TargetType="Label"> <Setter Property="FontSize" Value="26"/> <Setter Property="HorizontalTextAlignment" Value="Center"/> <Setter Property="FontFamily" Value="BrandonGrotesque-Regular"/> </Style> <Style x:Key="GreyButtonStyle" TargetType="Button"> <Setter Property="TextColor" Value="#7F8592"/> <Setter Property="FontFamily" Value="OpenSans"/> </Style> </ResourceDictionary> </Application.Resources> </Application>
Custom Fonts Include the TTF in each project Set the FontFamily on Label Note* you will need a custom renderer for pickers
Custom Renderers
Custom Renderers [assembly: ExportRenderer(typeof(Label), typeof(FontLabelRenderer))] namespace FBPlay.Droid { public class FontLabelRenderer : LabelRenderer { protected override void OnElementChanged(ElementChangedEventArgs<Label> e) { base.OnElementChanged(e); if (e.OldElement == null) { var customLabel = this.Element as CustomLabel; if (customLabel != null) { var lineSpacing = customLabel.LineSpacing; this.Control.SetLineSpacing((float)lineSpacing, 1f); } }
Custom Renderers [assembly: Xamarin.Forms.ExportRenderer(typeof(GoogleMapControl), typeof(GoogleMapControlRendererDroid))] namespace NHDS.Droid { public class GoogleMapControlRendererDroid : ViewRenderer<GoogleMapControl, Android.Views.View>, IOnMapReadyCallback, Android.Gms.Maps.GoogleMap.IInfoWindowAdapter, Android.Gms.Maps.GoogleMap.IOnInfoWindowClickListener, Android.Gms.Maps.GoogleMap.IOnCameraChangeListener, Android.Gms.Maps.GoogleMap.IOnInfoWindowCloseListener, ClusterManager.IOnClusterItemClickListener, ClusterManager.IOnClusterClickListener { MapView _mapView; Android.Gms.Maps.GoogleMap _googleMap; ClusterManager _clusterManager; Marker _homeMarker; protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<GoogleMapControl> e) { base.OnElementChanged(e); if (e.NewElement != null && Element.MapViewContainer != null) {
Effects A single effect can be attached to any control A ‘lite’ version of a custom renderer Change properties on the native control Optional - Per Platform Composition over inheritance (added as a feature) Examples: DismissKeyboardEffect, UnderlineEffect, BorderEffect, ShadowEffect
Native Embedding - Xaml <StackLayout> <androidWidget:TextView x:Arguments="{x:Static formsandroid:Forms.Context}" Text="{Binding LabelName}" /> <ios:UITextView Text="{Binding LabelName, Mode=TwoWay, UpdateSourceEventName=Ended}" /> <iosColorPicker:ColorPickerView SelectedColor="{Binding Path=FormsSelectedColor, Mode=TwoWay, Converter={StaticResource colorConverterIOS}, > </iosColorPicker:ColorPickerView> </StackLayout>
Handling Different Screen Sizes XAML <Style x:Key="FixSizeImage" TargetType="local:SvgImage"> <Setter Property="HeightRequest" > <OnIdiom x:TypeArguments="x:Double" Phone="300" Tablet="600" /> </Setter> <Setter Property="WidthRequest" > <OnIdiom x:TypeArguments="x:Double" Phone="280" Tablet="400" /> </Setter> </Style> Code if (Device.Idiom == TargetIdiom.Tablet) { DoubleListViewModel.FontSize = 20; }
Different Screen Sizes public partial class App : Application { public static double Width { get; set; } public static double Height { get; set; } public static void SetWidthHeight(double width, double height) { Width = width; Height = height; }
Different Screen Sizes public partial class IntroPage : ContentPage { public IntroPage() { InitializeComponent(); this.SizeChanged += Handle_SizeChanged; } void Handle_SizeChanged(object sender, EventArgs e) { App.SetWidthHeight(Width, Height); }
Different Screen Sizes Avoid Static Widths and Height (Use the Layouts) Design for Phone First
Xamarin.Forms Performance Tips
ListView Cell Recycling Recycles old cells and updates the BindingContext Huge Performance Improvements using System; namespace Xamarin.Forms { public enum ListViewCachingStrategy { RetainElement, RecycleElement } }
DataTemplateSelector Supports cell recycling Allows multiple views/types of cells public class MessageViewDataTemplateSelector : DataTemplateSelector { public DataTemplate IncomingTemplate { get; set; } public DataTemplate OutgoingTemplate { get; set; } protected override DataTemplate OnSelectTemplate(object item, BindableObject container) { return item is IncomingViewModel ? IncomingTemplate : OutgoingTemplate; } }
XamlC - Compiled Xaml XAML XAMLC Compile-time Runtime Parsed and inflated Parsed & turned into IL XAMLC
Performance - Simplify Layout / Reduce Nesting Know Your Controls
Performance - Know your controls Avoid Relative Layout Grid helps reduce nesting and layout cycles Don’t put a ListView inside Grid/ScrollView, use Header and Footer Don’t use nested StackLayouts when you can use a Grid Don’t use a Grid when you can use a StackLayout Don’t use a StackLayout as a ListView Use Star* and Static Widths on Grid Prefer using LayoutOptions.Fill, which are the defaults
Layout System
Layout Cycle Invalidation Cycle
Invalidation Cycle
Invalidation Cycle Layout/Page.cs (when child added) VisualElement.cs (Base class of Layouts, Views and Page) public event EventHandler MeasureInvalidated; Layout/Page.cs (when child added) view.MeasureInvalidated += OnChildMeasureInvalidated; Layout/Page.cs (on child invalidation) internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger) ….. conditional logic … MeasureInvalidated?.Invoke(this, new InvalidationEventArgs(trigger));
+= OnChildMeasureInvalidate Page StackLayout += OnChildMeasureInvalidate Label += OnChildMeasureInvalidate Label += OnChildMeasureInvalidate Label += OnChildMeasureInvalidate
Page StackLayout Label
Layout Cycle
Measure Layout Page StackLayout Label Label Label
Page Stack Grid Label Label
Why do we care? HACK IT! Page Stack Grid Label Page Stack Grid Label
Layout System Demo
Now it makes sense Reduce Nesting Don’t change the defaults of HorizontalOptions or VerticalOptions, Fill is good Use Grid but avoid ‘Auto’ Don’t use a StackLayout as a ListView
Pages, Layouts and Controls
Xamarin.Forms: Pages Content MasterDetail Navigation Tabbed Carousel
Pages Demo
Xamarin.Forms: Layouts Stack Absolute Relative Grid ContentView ScrollView Frame
Grid Not difficult to use (nearly as easy as StackLayout) Works well with XAML (unlike relative) Can be used to create overlays/advanced UIs Easy to reduce nesting Easy to reduce layout cycles (Star and Static Widths/Heights) Can do percentages with 2*
Grid Demo
ListView with Cell Recycling Ok… I’ve already talked about this…. but it’s seriously good.. Much faster now with Cell Recycling Use the DataTemplateSelector for different cell styles Alternative to Grid when there’s a dynamic numbers of rows Avoid add/remove views when binding context change
Memory Management
Memory Management Xamarin.Forms+Android+Memory == :( Use FFImageLoading, even for SVG’s (as all in all they use Bitmaps internally) and caching bitmaps is a huge thing. Use `using` blocks whenever possible, always dispose streams and heavy objects. Avoid using events or always unsubscribe them. Can turn binding to events is using behaviours https://blog.xamarin.com/turn-events-into-commands-with-behaviors/ It’s ok to call GC.Collect(); on Android after you’ve released a large amount of memory. https://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/
Xamarin Profiler
Animations
Post Layout - Translations and Animations Very Fast & Smooth Post Layout It will not cause all other elements to Layout Translations will not ‘reset’ during layout cycles Can be combined to create complex Animations Pro-Tip - don’t use LayoutTo it’s not PostLayout, it will reset during a layout cycle
Post Layout - Translations and Animations TranslateTo ScaleTo RelScaleTo RotateTo RelRotateTo RotateXTo RotateYTo FadeTo
Post Layout - Translations and Animations Control Timings Easing Methods (BounceIn, CubicIn, etc) Combined and control with TPL/async Complex/Parent-Child Animations
Animations Demo
Continue your Ignite learning path Visit Channel 9 to access a wide range of Microsoft training and event recordings https://channel9.msdn.com/ Head to the TechNet Eval Centre to download trials of the latest Microsoft products http://Microsoft.com/en-us/evalcenter/ Visit Microsoft Virtual Academy for free online training visit https://www.microsoftvirtualacademy.com
Win a Spark After Dark drone pilot pass by completing your session evaluation ASAP #MSAUIGNITE
Thank you Chat with me in the Speaker Lounge (all day today) Find me @rid00z or michaelridland.com