Xamarin.Forms – przykładowa aplikacja mobilna
Etap 1 – instalacja i założenie projektu
Tworzenie każdej aplikacji Xamarin, w tym naszej przykładowej aplikacji mobilnej, należy zacząć od zainstalowania Xamarina na każdym potrzebnym komputerze. Pod adresem http://xamarin.com/download znajduje się oprogramowanie instalujące wszystkie wymagane rzeczy, takie jak Xamarin, Xamarin Studio, integrację z Visual Studio kończąc na SDK platform mobilnych. Chcąc programować na 3 największe platformy Android, iOS i Windows Phone konieczne są co najmniej dwa komputery, jeden z Windowsem 8 lub 10 który umożliwia obsługę Androida i Windows Phone, oraz drugi Mac firmy Apple który umożliwia obsługę Androida i iOS. Ponieważ potrzeba będzie często przełączać się pomiędzy dwoma różnymi komputerami, nieodzownym elementem jest program typu GIT.
Z poziomu Visual Studio należy przejść do zakładania nowego projektu poprzez wybranie menu File->New->Project, z zainstalowanych szablonów wybrać język C#, następnie Mobile Apps oraz Blank App(Xamarin.Forms Portable), uzupełnić nazwę i ścieżkę projektu.
Została stworzona solucja z 4 projektami, pierwsza z kodem wspólnym, oraz kolejne dla każdej z platform. Wybór aktualnej platformy odbywa się poprzez kliknięcie prawym przyciskiem na projekcie specyficznej platformy w oknie Solution Explorer i wybranie Set as StartUp Project. W części wspólnej należy stworzyć 4 foldery Model, Service, View i ViewModel. Tak przygotowany projekt można traktować jako bazę dla każdej aplikacji Xamarin.Forms.
Etap 2 – pierwsza strona
W celu stworzenia pierwszej strony należy kliknąć prawym na folder View, wybrać Add->NewItem, następnie Forms Xaml Page i podać nazwę MainPage.cs
W pliku MainPage.xaml należy uzupełnić kod pierwszej strony.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Demo.View.MainPage"> <StackLayout> <Label Text="Główna strona aplikacji demo" /> <Entry Text="" Placeholder="Wprowadź tekst" /> <Label Text="Wprowadzono 0 znaków" /> <Button Text="Oblicz magiczny tekst" /> <Label Text="Nie obliczono" /> <Button Text="Przejdź do drugiej strony" /> </StackLayout> </ContentPage>
Kod pliku MainPage.xaml.cs
using Xamarin.Forms; namespace Demo.View { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); NavigationPage.SetHasNavigationBar(this, false); } } }
Kod pliku App.cs
using Demo.View; using Xamarin.Forms; namespace Demo { public class App : Application { public static INavigation Navigation = null; public App() { NavigationPage page = new NavigationPage(new MainPage()); App.Navigation = page.Navigation; MainPage = page; } protected override void OnStart() { // Handle when your app starts } protected override void OnSleep() { // Handle when your app sleeps } protected override void OnResume() { // Handle when your app resumes } } }
Tak zaprogramowany projekt można już uruchomić na dowolnej z platform, na fizycznym smartfonie lub emulatorze.
Etap 3 – MVVM
Aplikacje Xamarin.Forms zoptymalizowane są pod wzorzec projektowania MVVM tzn. Model-View-ViewModel. Oznacza to że każdy Page reprezentujący View powiązany jest z odpowiadającym mu ViewModel. ViewModel zawiera wiele pól i poleceń, pod które bindują się kontrolki z View, które obserwują je i reagują na ich zmiany. Cały mechanizm oparty jest na interfejsie INotifyPropertyChanged, dlatego implementację MVVM najlepiej zacząć od stworzenia pliku BaseViewModel, który będzie podstawą dla każdego pliku ViewModel.
using System.ComponentModel; using System.Runtime.CompilerServices; namespace Demo.ViewModel { public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } }
Należy teraz stworzyć nowy plik MainViewModel, pod który zbinduje się MainPage.
using Demo.View; using System.Windows.Input; using Xamarin.Forms; namespace Demo.ViewModel { public class MainViewModel : BaseViewModel { string _normalText = ""; string _magicText = "Nie obliczono"; public MainViewModel() { this.DoMagicCommand = new Command(() => { this.MagicText = string.Format("Magicznie powiększony tekst: {0}", this.NormalText.ToUpperInvariant()); }, () => { return !string.IsNullOrEmpty(this.NormalText); }); this.NextPageCommand = new Command(() => { //TODO }); } public string NormalText { set { if (_normalText != value) { _normalText = value; OnPropertyChanged(); OnPropertyChanged("NormalTextLength"); OnPropertyChanged("NormalTextVisibility"); ((Command)this.DoMagicCommand).ChangeCanExecute(); } } get { return _normalText; } } public string NormalTextLength { get { return string.Format("Wprowadzono {0} znaków", this.NormalText.Length); } } public bool NormalTextVisibility { get { return (this.NormalText.Length > 4); } } public string MagicText { set { if (_magicText != value) { _magicText = value; OnPropertyChanged(); } } get { return _magicText; } } public ICommand DoMagicCommand { protected set; get; } public ICommand NextPageCommand { protected set; get; } } }
Kod pliku MainPage.xaml.cs
using Demo.ViewModel; using Xamarin.Forms; namespace Demo.View { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); NavigationPage.SetHasNavigationBar(this, false); this.BindingContext = new MainViewModel(); } } }
Kod pliku MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Demo.View.MainPage"> <StackLayout> <Label Text="Główna strona aplikacji demo" /> <Entry Text="{Binding NormalText}" Placeholder="Wprowadź tekst" /> <Label Text="{Binding NormalTextLength}" /> <Label Text="Tekst ma więcej niż 4 znaki!" IsVisible="{Binding NormalTextVisibility}" /> <Button Text="Oblicz magiczny tekst" Command="{Binding DoMagicCommand}" /> <Label Text="{Binding MagicText}" /> <Button Text="Przejdź do drugiej strony" Command="{Binding NextPageCommand}" /> </StackLayout> </ContentPage>
Można teraz przetestować obecne działanie aplikacji.
Etap 4 – więcej niż jedna strona
Kolejnym krokiem będzie dodanie kolejnej strony oraz nawigacji pomiędzy stronami aplikacji. W tym celu należy dodać nową Forms Xaml Page o nazwie SecondPage.cs
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Demo.View.SecondPage"> <StackLayout> <Label Text="Druga strona" /> <Label Text="{Binding LangCode}" /> </StackLayout> </ContentPage>
Nowa klasa SecondViewModel
namespace Demo.ViewModel { public class SecondViewModel : BaseViewModel { string _langCode = ""; public SecondViewModel() { this.LangCode = string.Format("Zaraz wykryjemy Twój język"); } public string LangCode { set { if (_langCode != value) { _langCode = value; OnPropertyChanged(); } } get { return _langCode; } } } }
Kod pliku SecondPage.xaml.cs
using Demo.ViewModel; using Xamarin.Forms; namespace Demo.View { public partial class SecondPage : ContentPage { public SecondPage() { InitializeComponent(); NavigationPage.SetHasNavigationBar(this, false); this.BindingContext = new SecondViewModel(); } } }
Fragment pliku MainViewModel.cs
this.NextPageCommand = new Command(async () => { await App.Navigation.PushAsync(new SecondPage()); });
Można teraz przetestować obecne działanie aplikacji.
Etap 5 – odwołania do natywności
Ostatnim krokiem jest przykład usługi która potrzebuje odwołania do specyficznych cech każdej z platform, tzw. natywności. W tym celu należy utworzyć w folderze Service interfejs ILangService
namespace Demo.Service { public interface ILangService { string GetLangCode(); } }
Dodatkowo należy stworzyć implementację tego interfejsu w każdym projekcie specyficznym dla platform. Kod pliku LangService dla Androida
using Demo.Service; using Xamarin.Forms; [assembly: Dependency(typeof(Demo.Droid.LangService))] namespace Demo.Droid { public class LangService : ILangService { public string GetLangCode() { var androidLocale = Java.Util.Locale.Default; return androidLocale.ToString().Replace("_", "-").TrimEnd('-'); } } }
Kod pliku LangService dla iOS
using Demo.Service; using Foundation; using Xamarin.Forms; [assembly: Dependency(typeof(Demo.iOS.LangService))] namespace Demo.iOS { public class LangService : ILangService { public string GetLangCode() { var language = "en"; if (NSLocale.PreferredLanguages.Length > 0) { var pref = NSLocale.PreferredLanguages[0]; language = pref.Replace("_", "-"); } return language; } } }
Kod pliku LangService dla Windows Phone
using Demo.Service; using Xamarin.Forms; [assembly: Dependency(typeof(Demo.WinPhone.LangService))] namespace Demo.WinPhone { public class LangService : ILangService { public string GetLangCode() { return System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; } } }
Fragment pliku SecondViewModel.cs
public SecondViewModel() { ILangService langService = DependencyService.Get<ILangService>(); this.LangCode = string.Format("Twój język to: {0}", langService.GetLangCode()); }
Można teraz przetestować obecne działanie aplikacji.
Podsumowanie
Gotowy projekt przykładowej aplikacji dostępny jest pod adresem https://github.com/Ermlab/xamarin-demo-page