Xamarin lectures for students
Для взаимодействия с веб-сервисами в .NET используется класс HttpClient. С помощью его методов можно посылать определенный запрос к серверу.
По умолчанию проект Xamarin.Forms не содержит функционала HttpClient, поэтому его надо вручную добавлять в проект через NuGet установив System.Net.Http
HttpClient предоставляет следующие методы:
GetAsync(): отправляет запрос типа GetDeleteAsync(): отправляет запрос типа DeletePostAsync(): отправляет запрос типа PostPutAsync(): отправляет запрос типа PutSendAsync(): отправляет запрос любого типа в зависимости от настроек заголовков запроса. Выше определенные методы являются частными случаями данного методаGetByteArrayAsync(): отправляет массив байтов в запросе типа GetGetStreamAsync(): отправляет поток Stream в запросе типа GetGetStringAsync(): отправляет строку в запросе типа GetПри отправке запроса для его настройки мы можем применять объект класса HttpRequestMessage. В частности, для конфигурации запроса можно использовать следующие свойства класса:
Content: отправляемые данныеHeaders: заголовки httpMethod: метод отправки(Get/Post/Put/Delete)RequestUri: адрес сервераПосле выполнения запроса мы можем получить ответ в виде объекта класса HttpResponseMessage. Для обработки ответа мы можем воспользоваться следующими свойствами данного класса:
Content: полученные данныеHeaders: заголовки ответа, полученные от сервераStatusCode: статусный код ответаНапример:
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("http://somesite.com");
request.Method = HttpMethod.Get;
request.Headers.Add("Accept", "application/json");
HttpResponseMessage response = await client.SendAsync(request);
if(response.StatusCode==HttpStatusCode.OK)
{
HttpContent responseContent = response.Content;
var json = await responseContent.ReadAsStringAsync();
}
Если сервер отправляет нам данные в виде json, то, получив данные в виде строки с помощью метода responseContent.ReadAsStringAsync(), мы можем их десериализовать из json с помощью специальных инструментов или библиотек, как Json.NET.
Используя методы GetAsync()/PostAsync()/PutAsync()/DeleteAsync() соответственно уже не надо указывать метод запроса.
Например:
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://somesite.com");
if(response.StatusCode==HttpStatusCode.OK)
{
HttpContent responseContent = response.Content;
var json = await responseContent.ReadAsStringAsync();
}
Если сервер посылает ответ в виде простой строки, массива байт или файла, то в данном случае проще использовать методы GetStringAsync()/GetByteArrayAsync()/GetStreamAsync(). Например, получим от сервера строку:
HttpClient client = new HttpClient();
string response = await client.GetStringAsync("http://somesite.com");
В то же время в данном случае мы не сможем отследить статусный код ответа от сервера.
Данные запроса и ответа представлены в виде объекта класса HttpContent. Этот класс является абстрактным, и фактически мы будем работать с его производными классами:
StringContent: применяется, если отправка или получение данных происходит в виде строкиByteArrayContent: применяется, если отправка или получение данных происходит в виде массива байтовStreamContent: применяется, если отправка или получение данных происходит, как правило, в виде файла, например, изображенияЕсли мы хотим получить данные в виде строки, массива байт или файла, то соответственно мы можем использовать один из методов GetStringAsync()/GetByteArrayAsync()/GetStreamAsync(), либо можно обрабатывать свойство Content объекта HttpResponseMessage.
Для отправки данных мы можем задействовать свойство Content объекта HttpRequestMessage:
var friend = new Friend { Id = 1, Name = "Иван Иванов" };
// сериализация объекта с помощью Json.NET
string json = JsonConvert.SerializeObject(friend);
HttpContent content = new StringContent(json);
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("http://somesite.com");
request.Method = HttpMethod.Post;
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);
Либо в качестве альтернативы для отправки данных можно использовать специальные методы PostAsync()/PutAsync():
var friend = new Friend { Id = 1, Name = "Иван Иванов" };
// сериализация объекта с помощью Json.NET
string json = JsonConvert.SerializeObject(friend);
HttpContent content = new StringContent(json);
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.PostAsync("http://somesite.com", content);
Одним из ключевых моментов работы многих мобильных приложений является возможность взаимодействия с сервером - отправка или получение данных. И в Xamarin.Forms мы тоже можем взаимодействовать с сервером. Рассмотрим на примере получения данных в формате json.
В качестве примера возьмем бесплатный API от ЦБ РФ для получения текущих котировок валют. Так, для получения текущих значений для всех пар валют в данном API необходимо обратиться по адресу https://www.cbr-xml-daily.ru/daily_json.js. И в качестве ответа мы получим данные в формате json примерно следующего вида:
{
"Date": "2020-01-14T11:30:00+03:00",
"PreviousDate": "2020-01-11T11:30:00+03:00",
"PreviousURL": "\/\/www.cbr-xml-daily.ru\/archive\/2020\/01\/11\/daily_json.js",
"Timestamp": "2020-01-13T23:00:00+03:00",
"Valute": {
"AUD": {
"ID": "R01010",
"NumCode": "036",
"CharCode": "AUD",
"Nominal": 1,
"Name": "Австралийский доллар",
"Value": 42.1086,
"Previous": 42.1246
},
...
}
Теперь посмотрим, как мы эти данные можем получать и отображать в приложении на Xamarin Forms.
Создадим новый проект и вначале добавим в него класс, который будет представлять загружаемые данные. Назовем этот класс RateInfo:
using System;
namespace RestApp
{
public class RateInfo
{
public string ID { get; set; }
public string NumCode { get; set; }
public string CharCode { get; set; }
public int Nominal { get; set; }
public string Name { get; set; }
public double Value { get; set; }
public double Previous { get; set; }
}
}
Как можно заметить, данный класс соответствует части ответа от сервера (токену) в формате json:
"AUD": {
"ID": "R01010",
"NumCode": "036",
"CharCode": "AUD",
"Nominal": 1,
"Name": "Австралийский доллар",
"Value": 42.1086,
"Previous": 42.1246
}
Так как значение Valute не является массивом, то необходимо создать класс, описывающий каждый его элемент:
using System;
namespace RestApp
{
public class ValuteInfo
{
public RateInfo AUD { get; set; }
public RateInfo AZN { get; set; }
public RateInfo GBP { get; set; }
public RateInfo AMD { get; set; }
//...
}
}
Что соответствует следующей json:
"Valute": {
"AUD": {
...
},
"AZN": {
...
},
"GBP": {
...
},
"AMD": {
...
},
...
}
Финальный класс:
using System;
namespace RestApp
{
public class ValuteData
{
public string Date { get; set; }
public string PreviousDate { get; set; }
public string PreviousURL { get; set; }
public string Timestamp { get; set; }
public ValuteInfo Valute { get; set; }
}
}
Что соответствует следующей json:
{
"Date": "2020-01-14T11:30:00+03:00",
"PreviousDate": "2020-01-11T11:30:00+03:00",
"PreviousURL": "\/\/www.cbr-xml-daily.ru\/archive\/2020\/01\/11\/daily_json.js",
"Timestamp": "2020-01-13T23:00:00+03:00",
"Valute": {
...
}
}
Затем поскольку мы загружаем данные в формате json, то нам надо будет десериализовать эти данные. Для этого будем использовать библиотеку Newtonsoft.Json, которую добавим в проект через Nuget:

Полученный json, можно десериализировать с помощью DeserializeObject:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
var response = await client.GetAsync(client.BaseAddress);
response.EnsureSuccessStatusCode(); // выброс исключения, если произошла ошибка
// десериализация ответа в формате json
var content = await response.Content.ReadAsStringAsync();
var rate = JsonConvert.DeserializeObject<ValuteData>(content);
Также можно десериализовать лишь часть json, перед этим необходимо выбрать необходимый токен с помощью SelectToken:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
var response = await client.GetAsync(client.BaseAddress);
response.EnsureSuccessStatusCode(); // выброс исключения, если произошла ошибка
// десериализация ответа в формате json
var content = await response.Content.ReadAsStringAsync();
JObject o = JObject.Parse(content);
var str = o.SelectToken(@"$.Valute.USD");
var rateInfo = JsonConvert.DeserializeObject<RateInfo>(str.ToString());
Также можно не создавать отдельные классы для каждого отдельного токена и воспользоваться JObject.Parse и далее обращаться к каждому элементу как к части словаря:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
var response = await client.GetAsync(client.BaseAddress);
response.EnsureSuccessStatusCode(); // выброс исключения, если произошла ошибка
// десериализация ответа в формате json
var content = await response.Content.ReadAsStringAsync();
JObject o = JObject.Parse(content);
var rate = o["Valute"]["USD"];
Если вы хотите использовать ручной парс, либо вы используете кастомный формат данных, то можно прочитать how to
За основу контейнера компоновки FlexLayout была взята концепция FlexBox - модуля CSS, который обеспечивает гибкую настройку размещения вложенных элементов.
Свойство Direction устанавливает режим направления элементов: по горизонтали в строку или по вертикали в столбик.
В качестве значения оно принимает одно из значений перечисления FlexDirection:
Row: расположение в строкуRowReverse: расположение в строку в обратном порядкеColumn: расположение в столбецColumnReverse: расположение в столбец в обратном порядкеРасположим элементы в строку:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutApp"
x:Class="FlexLayoutApp.MainPage">
<FlexLayout Direction="Row">
<Button BackgroundColor="SeaGreen" Text="Item 1" />
<Button BackgroundColor="LightBlue" Text="Item 2" />
<Button BackgroundColor="LightPink" Text="Item 3" />
<Button BackgroundColor="LightGreen" Text="Item 4" />
</FlexLayout>
</ContentPage>
При таком расположении по умолчанию высота каждого элемента равна высоте контейнера, а ширина имеет значение, необходимое для отображения данного элемента. Однако с помощью свойств HeightRequest и WidthRequest мы можем настроить соответственно высоту и ширину элемента.
<FlexLayout Direction="Row">
<Button BackgroundColor="SeaGreen" Text="Item 1" HeightRequest="100" WidthRequest="90"/>
<Button BackgroundColor="LightBlue" Text="Item 2" HeightRequest="90" WidthRequest="100" />
<Button BackgroundColor="LightPink" Text="Item 3" HeightRequest="90" WidthRequest="100" />
<Button BackgroundColor="LightGreen" Text="Item 4" HeightRequest="100" WidthRequest="90"/>
</FlexLayout>
Однако если совокупные размеры элементов превосходят доступное пространство контейнера, то элементы будут масштабироваться таким образом, чтобы поместиться в контейнере. Например:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutApp"
x:Class="FlexLayoutApp.MainPage">
<ContentPage.Resources>
<Style x:Key="btnStyle" TargetType="Button">
<Setter Property="WidthRequest" Value="120" />
<Setter Property="HeightRequest" Value="100" />
</Style>
</ContentPage.Resources>
<FlexLayout Direction="Row">
<Button BackgroundColor="SeaGreen" Text="Item 1" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightBlue" Text="Item 2" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightPink" Text="Item 3" Style="{StaticResource Key=btnStyle}"/>
<Button BackgroundColor="LightGreen" Text="Item 4" Style="{StaticResource Key=btnStyle}"/>
<Button BackgroundColor="LightCoral" Text="Item 5" Style="{StaticResource Key=btnStyle}"/>
<Button BackgroundColor="LightSalmon" Text="Item 6" Style="{StaticResource Key=btnStyle}"/>
<Button BackgroundColor="LightSteelBlue" Text="Item 7" Style="{StaticResource Key=btnStyle}"/>
</FlexLayout>
</ContentPage>
Теперь расположим элементы в столбик:
<FlexLayout Direction="Column">
<Button BackgroundColor="SeaGreen" Text="Item 1" />
<Button BackgroundColor="LightBlue" Text="Item 2" />
<Button BackgroundColor="LightPink" Text="Item 3" />
<Button BackgroundColor="LightGreen" Text="Item 4" />
</FlexLayout>
По умолчанию при размещении в столбик каждый элемент растягивается по всей ширине контейнера и принимает ту ширину, которая для него необходима, чтобы вместить его содержимое. Однако с помощью свойств HeightRequest и WidthRequest мы также можем управлять размерами элементов внутри контейнера.
Также можно комбинировать различные режимы направления элементов:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutApp"
x:Class="FlexLayoutApp.MainPage">
<ContentPage.Resources>
<Style x:Key="btnStyle" TargetType="Button">
<Setter Property="WidthRequest" Value="120" />
<Setter Property="HeightRequest" Value="100" />
</Style>
</ContentPage.Resources>
<FlexLayout Direction="Column">
<FlexLayout Direction="Row" HeightRequest="100">
<Button BackgroundColor="SeaGreen" Text="Item 11" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightBlue" Text="Item 12" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightPink" Text="Item 13" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightGreen" Text="Item 14" Style="{StaticResource Key=btnStyle}" />
</FlexLayout>
<FlexLayout Direction="Row" HeightRequest="100">
<Button BackgroundColor="LightCoral" Text="Item 21" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="SeaGreen" Text="Item 22" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightBlue" Text="Item 23" Style="{StaticResource Key=btnStyle}" />
<Button BackgroundColor="LightPink" Text="Item 24" Style="{StaticResource Key=btnStyle}" />
</FlexLayout>
</FlexLayout>
</ContentPage>
В данном случае FlexLayout содержит два других контейнера FlexLayout. Внешний контейнер размещает элементы в столбик, а вложенные контейнеры - в строку. Подобным образом мы можем создавать более сложные макеты элементов.