- PVSM.RU - https://www.pvsm.ru -
Друзья! Мы рады представить новый материал на тему разработки мобильных приложений на Xamarin. В новой статье мы рассмотрим, как в Xamarin.Forms реализовывать управлениями состояниями окон (идет загрузка данных, отсутствует интернет и другие) на XAML. Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn [1]
Подавляющее большинство современных бизнес-приложений активно взаимодействуют с внешними интернет-сервисами с целью получения данных для отображения. Помимо этого, часто возникают ситуации, в которых один и тот же экран может показывать разные наборы данных, вплоть до смены оформления.
Мобильные приложения, в отличие от веб-сайтов должны гораздо быстрее взаимодействовать с пользователем, поэтому показывать длительное время пустой экран во время загрузки данных, считается не очень правильным. Дополнительно, приложение должно уведомлять пользователя об ошибках загрузки данных или отсутствии интернет-соединения. Ленивые разработчики могут обойтись отображением всплывающих уведомлений в духе “Ошибка загрузки данных”, но мы пойдем другим путем.
Итак, давайте выделим основные состояния одного (!) экрана:
У программиста могут начать шевелиться волосы при мыслях о том, сколько кода надо будет написать, чтобы заменять содержимое одного экрана, при расчете, что таких экранов могут быть десятки, а каждое из состояний может быть достаточно сложным. Рано паниковать, простое и элегантное решение предложил Patrick McCurley [2]. Мы возьмем это решение за основу и немного доработаем.
В основе данного подхода лежит идея описывать все состояния экрана в XAML и управлять их сменой с помощью ViewModel. Забегая вперед, отметим, что решение достаточно простое и может быть использовано не только для управлениями состояниями всего окна, но и отдельных его частей.
Вот так будет выглядеть XAML-описание одной страницы с поддержкой смены состояний:
<?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>
Просто и понятно. При этом крупные блоки для состояний можно вынести в виде отдельных View для повторного использования.
Вот так будет описан враппер для одного состояния:
[ContentProperty("Content")]
public class StateCondition : View
{
public object State { get; set; }
public View Content { get; set; }
}
А вот перечисление (enum) возможных состояний экрана:
public enum States
{
Loading,
Normal,
Error,
NoInternet,
NoData
}
Мы немного доработали State Container от Patrick McCurley, добавив простые анимации смены состояния, чтобы все работало плавно:
[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}");
}
}
}
Для получения статуса интернет-соединения мы подключили библиотеку ConnectivityPlugin [3].
if (!CrossConnectivity.Current.IsConnected)
{
State = States.NoInternet; // Меняем свойство у ViewModel
return;
}
Как видим, StateContainer это не надстройка над Page, а обычная ContentView и может вполне спокойно размещаться на экране со статическим или уже загруженным контентом. Это позволит реализовать механизмы частичной дозагрузки данных, например, когда у нас уже есть название и ссылка на фотографию, которые можно отображать пользователю без необходимости ожидания.
Итак, сегодня мы рассмотрели работу с состояниями экранов с помощью простого и элегантного StateContainer, который позволяет улучшить пользовательский опыт. А простые анимации смены состояний добавляют плавности и придают приложению законченный вид.
В следующей статье мы рассмотрим вопросы интеграции с внешним REST API с помощью Refit [4], ModernHttpClient [5] и Polly [6].
Вячеслав Черников — руководитель отдела разработки компании Binwell [7]. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.
Автор: Microsoft
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/174358
Ссылки в тексте:
[1] #xamarincolumn: https://habrahabr.ru/search/?target_type=posts&q=%5Bxamarincolumn%5D&order_by=date
[2] Patrick McCurley: https://github.com/xDelivered-Patrick/Xamarin.Forms.Essentials
[3] ConnectivityPlugin: https://github.com/jamesmontemagno/ConnectivityPlugin
[4] Refit: https://github.com/paulcbetts/refit
[5] ModernHttpClient: https://github.com/paulcbetts/ModernHttpClient
[6] Polly: https://github.com/App-vNext/Polly
[7] Binwell: http://www.binwell.com/
[8] по ссылке: https://habrahabr.ru/company/microsoft/blog/281142/
[9] бесплатные предложения для разработчиков: https://www.visualstudio.com/ru-ru/products/free-developer-offers-vs.aspx
[10] Visual Studio Dev Essentials: https://www.visualstudio.com/ru-ru/products/visual-studio-dev-essentials-vs.aspx
[11] Источник: https://habrahabr.ru/post/307890/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.