[Из песочницы] Xamarin.Forms Shell
В конце мая Microsoft выпустила в релиз Xamarin.Forms Shell — оболочку нацеленную на упрощение создания кроссплатформенных мобильных приложений и включающий в себя следующий функционал: боковое меню, вкладки, навигация, поиск.
Давайте начнем с создания пустого проекта Xamarin.Forms в Visual Studio 2019. Обратите внимание, на данный момент Shell официально поддерживает только 2 платформы: iOS и Android, UWP еще в стадии разработки. Рекомендую сразу же обновить все nuget пакеты в решении.
Далее создадим производный от Shell класс AppShell, для этого в добавим XAML-файл в общий проект со следующим содержимым:
AppShell.xaml
AppShell.xaml.cs
namespace HelloShell
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}
}
после чего в файле App.xaml.cs указываем что в качестве MainPage у нас будет выступать AppShell:
public App()
{
InitializeComponent();
//MainPage = new MainPage();
MainPage = new AppShell();
}
и пару ContentPage страниц: Page1 и Page2. Так же в нашем тестовом приложении будут использоваться изображения, поэтому добавим их в платформозависимые проекты, для андройд в папку Resources=>drawable, а для ios в папку Resources.
Боковое меню
Боковое меню (часто его называют гамбургер меню) представляет из себя выезжающее меню, которое можно вызвать по нажатию на кнопку или специальным жестом и включает в себя заголовок (Header), список страниц (Flyout Items) и меню (Flyout Menu)
AppShell.xaml
AppShell.xaml.cs
namespace HelloShell
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
private async void MenuItem_Clicked(object sender, System.EventArgs e)
{
await DisplayAlert("","Привет Хабр!","OK");
}
}
}
Вкладки
Xamarin.Forms Shell в качестве корневого шаблона может поддерживать нижние и верхние вкладки, а так же их комбинацию:
AppShell.xaml.cs
Как видите, сделать это довольно просто. Еще одним преимуществом является эффективная загрузка страниц, которая позволяет инициализировать страницу только когда на нее переходит пользователь, что существенно ускоряет запуск приложения.
Навигация
Xamarin.Forms предоставляет улучшенную навигацию по интерфейсу на основе URI, позволяя переходить на любую страницу в приложении без соблюдения строгой иерархии и переходить назад без необходимости прохода всех страниц в стеке навигации. Чтобы навигация работала, страницу нужно зарегистрировать, сделать это можно в разметке XAML в FlyoutItem, Tab и ShellContent с помощью свойства Route
...
или в коде
Routing.RegisterRoute("page1", typeof(Page1));
навигация осуществляется с помощью команды
await Shell.Current.GoToAsync("//page2");
В качестве примера внесем изменения в следующие файлы:
AppShell.xaml
MainPage.xaml
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void ToPage2(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//page2");
}
}
Page2.xaml
Page2.xaml.cs
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
}
private async void Back(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//main");
}
}
Поиск
Xamarin.Forms Shell имеет встроенные функции поиска, предоставляемые классом SearchHandler. Чтобы добавить функцию поиска на страницу, мы создадим класс PetSearchHandler производный от SearchHandler и переопределим методы OnQueryChanged и OnItemSelected. Метод OnQueryChanged срабатывает при вводе пользователем текста в поисковое поле и принимает два аргумента: oldValue и newValue, которые содержат предыдущий и новый поисковый запрос соответственно.
Метод SelectedItem выполняется в момент выбора пользователем результата поиска и принимает в качестве параметров объект, в данном случае Animal.
Для примера создадим модель Animal
Models/Animal.cs
public class Animal
{
public string Name { get; set; }
public string ImageUrl { get; set; }
}
Класс PetData который будет содержать коллекцию наших любимых кошечек и собачек
Data/PetData.cs
public static class PetData
{
public static IList Pets { get; private set; }
static PetData()
{
Pets = new List();
Pets.Add(new Animal
{
Name = "Afghan Hound",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/6/69/Afghane.jpg"
});
Pets.Add(new Animal
{
Name = "Alpine Dachsbracke",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/2/23/Alpejski_gończy_krótkonożny_g99.jpg/320px-Alpejski_gończy_krótkonożny_g99.jpg"
});
Pets.Add(new Animal
{
Name = "American Bulldog",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/5/5e/American_Bulldog_600.jpg"
});
Pets.Add(new Animal
{
Name = "Abyssinian",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Gustav_chocolate.jpg/168px-Gustav_chocolate.jpg"
});
Pets.Add(new Animal
{
Name = "Arabian Mau",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/d/d3/Bex_Arabian_Mau.jpg"
});
Pets.Add(new Animal
{
Name = "Bengal",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Paintedcats_Red_Star_standing.jpg/187px-Paintedcats_Red_Star_standing.jpg"
});
Pets.Add(new Animal
{
Name = "Burmese",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/0/04/Blissandlucky11.jpg"
});
Pets.Add(new Animal
{
Name = "Cyprus",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/CyprusShorthair.jpg/320px-CyprusShorthair.jpg"
});
Pets.Add(new Animal
{
Name = "German Rex",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/c/c7/German_rex_harry_%28cropped%29.jpg"
});
}
}
PetSearchHandler.cs
public class PetSearchHandler : SearchHandler
{
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);
if (string.IsNullOrWhiteSpace(newValue))
{
ItemsSource = null;
}
else
{
ItemsSource = PetData.Pets
.Where(pet => pet.Name.ToLower().Contains(newValue.ToLower()))
.ToList();
}
}
protected override async void OnItemSelected(object item)
{
base.OnItemSelected(item);
var pet = item as Animal;
if (pet is null) return;
await App.Current.MainPage.DisplayAlert("Вы выбрали",pet.Name,"ok");
}
}
Добавим страницу Pets в которой зададим наш PetSearchHandler
В итоге у нас должна получиться страница с поисковым полем в навигационном баре, при вводе поисковой фразы отображается простой список из названий питомцев.
При желании мы легко можем настроить содержимое ячейки списка, добавив туда картинку и несколько текстовых меток:
Исходники на github