Friends! We are pleased to present new material on the development of mobile applications on Xamarin.In the new article, we will look at how Xamarin.Forms implements window state management (data loading, Internet and other is in progress) on XAML.
All articles from the column can be found and read on the link #xamarincolumn
The vast majority of modern business applications actively interact with external Internet services in order to obtain data for display. In addition, there are often situations in which the same screen can show different data sets, up to a change of appearance.
Mobile applications, unlike websites, should interact with the user much faster, so displaying a blank screen for a long time while loading data is considered not very correct. Additionally, the application must notify the user of data download errors or the absence of an Internet connection. Lazy developers can do with the display of pop-up notifications in the spirit of “Error loading data”, but we will go the other way.
So, let's select the basic states of one (!) Screen:
A programmer may begin to move his hair when thinking about how much code will have to be written to replace the contents of one screen, while calculating that there may be dozens of such screens, and each of the states can be quite complicated. Early to panic, a simple and elegant solution offered by Patrick McCurley . We will take this decision as a basis and refine it a bit.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="ApiDemo.DemoPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:stateContainerDemo="clr-namespace:StateContainerDemo;assembly=ApiDemo"> <stateContainerDemo:StateContainer State="{Binding State}"> <stateContainerDemo:StateCondition State="Loading"> <ActivityIndicator IsRunning="True" /> </stateContainerDemo:StateCondition> <stateContainerDemo:StateCondition State="Normal"> <Label Text=" "/> </stateContainerDemo:StateCondition> <stateContainerDemo:StateCondition State="Error"> <StackLayout> <Label Text=" " /> <Button Command="{Binding LoadDataCommand}" Text="" /> </StackLayout> </stateContainerDemo:StateCondition> <stateContainerDemo:StateCondition State="NoInternet"> <StackLayout> <Label Text=" -" /> <Button Command="{Binding LoadDataCommand}" Text="" /> </StackLayout> </stateContainerDemo:StateCondition> <stateContainerDemo:StateCondition State="NoData"> <Label Text=" , " /> </stateContainerDemo:StateCondition> </stateContainerDemo:StateContainer> </ContentPage>
[ContentProperty("Content")] public class StateCondition : View { public object State { get; set; } public View Content { get; set; } }
public enum States { Loading, Normal, Error, NoInternet, NoData }
[ContentProperty("Conditions")] public class StateContainer : ContentView { public List<StateCondition> Conditions { get; set; } = new List<StateCondition>(); public static readonly BindableProperty StateProperty = BindableProperty.Create(nameof(State), typeof(object), typeof(StateContainer), null, BindingMode.Default, null, StateChanged); public static void Init() { //for linker } private static async void StateChanged(BindableObject bindable, object oldValue, object newValue) { var parent = bindable as StateContainer; if (parent != null) await parent.ChooseStateProperty(newValue); } public object State { get { return GetValue(StateProperty); } set { SetValue(StateProperty, value); } } private async Task ChooseStateProperty(object newValue) { if (Conditions == null && Conditions?.Count == 0) return; try { foreach (var stateCondition in Conditions.Where(stateCondition => stateCondition.State != null && stateCondition.State.ToString().Equals(newValue.ToString()))) { if (Content != null) { await Content.FadeTo(0, 100U); // Content.IsVisible = false; // await Task.Delay(30); // UI- } // stateCondition.Content.Opacity = 0; Content = stateCondition.Content; Content.IsVisible = true; await Content.FadeTo(1); break; } } catch (Exception e) { Debug.WriteLine($"StateContainer ChooseStateProperty {newValue} error: {e}"); } } }
if (!CrossConnectivity.Current.IsConnected) { State = States.NoInternet; // ViewModel return; }
As you can see, the StateContainer is not an add-on over the Page, but an ordinary ContentView and can easily be placed on the screen with static or already loaded content. This will allow the implementation of partial data reloading mechanisms, for example, when we already have a name and a link to a photo that can be displayed to the user without having to wait.
So, today we looked at working with screen states using a simple and elegant StateContainer, which allows us to improve the user experience. And simple animations of state transitions add smoothness and give the application a finished look.
In the next article, we will look at integrating with the external REST API using Refit , ModernHttpClient and Polly .
Source: https://habr.com/ru/post/307890/
All Articles