Xamarin.Android na trzy sposoby – Code Behind, MVP i MVVM.

Siemanko.

W dzisiejszym poście trzy sposoby pisania natywnej aplikacji dla androida w Xamarin.Android. Trzy zupełnie identyczne aplikacje pod względem funkcjonalnośći i UI jednak zupełnie inne pod względem architektury kodu i struktury projeku. Sama aplikacja banalnie prosta bo wpis ten traktuje o podejściu do pisania aniżeli do pisania konkretnych funkcjonalności.

Screenshot_2017-05-01-10-48-54

Tak. Jeden guzik i pole tekstowe do wyświetlania liczby dotychczasowych kliknięć.

Jedziemy.

Code Behind

Tu obsługę zdarzeń na widoku oraz logikę biznesową piszemy w bezpośrednio w Activity.cs.

[Activity(Label = "AndroidCodeBehind", MainLauncher = true, Icon = "@drawable/icon")]
 public class MainActivity : Activity
 {
    private TextView _clicksCountTxt;
    private Button _helloWorldBtn;
    private int _clicksCount;

    protected override void OnCreate(Bundle bundle)
    {
       base.OnCreate(bundle);
       SetContentView(Resource.Layout.Main);
       SetView();
    }

    private void SetView()
    {
       _clicksCountTxt = FindViewById(Resource.Id.clicksCountTxt);

       _helloWorldBtn = FindViewById(Resource.Id.helloWorldBtn);
       _helloWorldBtn.Click += (s, e) =>
       {
          _clicksCount++;
          _clicksCountTxt.Text = _clicksCount.ToString();
       };
    }
 }

To jest najszybsze i najprostsze rozwiązanie lecz najmniej eleganckie i nieprofesjonalne. Przy dużej ilości zdarzeń na widoku i konieczności ich obsługi b. szybko zrobi się bałagan i przede wszystkim taki kod jest właściwie nietestowalny.

MVP

Wzorzecz Model View Presenter pozwala na odseparowanie warstwy prezentacji od logiki biznesowej. Widoki implementują interfejs, w którym są zadeklarowane wszystkie właściwości i eventy, a prezenter subskrybuje te zdarzenia i w odpowiedzi na nie ustawia konkretne property.

Interfejs widoku

public interface IMainActivityView
{
   event EventHandler ButtonClicked;
   int ClicksCount { get; set; }
}

MainActivity musi implementowac w.w. interfejs.

[Activity(Label = "AndroidMVP", MainLauncher = true, Icon = "@drawable/icon")]
 public class MainActivity : Activity, IMainActivityView
 {
    private TextView _clicksCountTxt;
    private Button _helloWorldBtn;
    private MainActivityPresenter _presenter;
    public event EventHandler ButtonClicked;

    public int ClicksCount
    {
       get => int.Parse(_clicksCountTxt.Text);
       set => _clicksCountTxt.Text = value.ToString();
    }

    protected override void OnCreate(Bundle bundle)
    {
      base.OnCreate(bundle);
      SetContentView(Resource.Layout.Main);
      SetView();
    }

   private void SetView()
   {
      _clicksCountTxt = FindViewById(Resource.Id.clicksCountTxt);
    
       _helloWorldBtn = FindViewById(Resource.Id.helloWorldBtn);
       _helloWorldBtn.Click += (s, e) =>
       {
          ButtonClicked?.Invoke(this, EventArgs.Empty);
       };

       _presenter = new MainActivityPresenter(this);
    }
 }

Zostaje jeszcze prezenter

public class MainActivityPresenter
 {
    private readonly IMainActivityView _view;

    public MainActivityPresenter(IMainActivityView view)
    {
       _view = view;
       _view.ButtonClicked += _view_ButtonClicked;
    }

    private void _view_ButtonClicked(object sender, EventArgs e)
    {
       _view.ClicksCount++;
    }
 }

Teraz widok tylko rzuca zdarzenie informujące o kliknięciu przycisku i nie jest w żaden sposób odpowiedzialny za to co ma się z tym wiązać. Całą logikę wykonuje prezenter i na koniec ustawia wyliczoną liczbą kliknięć. Taki kod jest już testowalny bo wystarczy, że napiszemy fake’ową implementację dla interfejsu widoku i już możemy testować działanie prezentera. Idąc dalej, taką aplikację mogą swobodnie programować dwie osoby – jedna UI, a druga logikę biznesową – wystarczy, że wspólnie uzgodnią kontrakt, który wprowadza intefejs widoku i w żaden sposób nie powinni sobie przeszkadzać na takim poziomie abstrakcji.

MVVM

Tutaj użycie patternu MvvM jest możliwe dzięki framework’owi MvvM Cross. Jest to na prawdę potężny framework dający olbrzymie możliwości oraz wygodę związaną z tzw. two way data binding. Tutaj kod naszego Activity jest najkrótszy.

[Activity(MainLauncher = true, Icon = "@drawable/icon")]
 public class MainView : MvxActivity
 {
    protected override void OnCreate(Bundle bundle)
    {
       base.OnCreate(bundle);
       SetContentView(Resource.Layout.Main);
    }
 }

Tak. Nic tu nie ma poza wskazaniem layout’u do wyrenderowania. Tym razem zmienił się nieco kod samego layout’u, a mianowicie zostały dodane do kontrolek atrybuty dzięki, który Mvvm Cross może zbindować własciwości bądź zdarzenia do naszego ViewModelu.

Dla buttona

local:MvxBind=”Click IncreaseCount”

Dla text view

local:MvxBind=”Text ClicksCount, Converter=StringToIntConverter”

Natomiast kod naszego ViewModelu wygląda tak:

public class MainViewModel : MvxViewModel
 {
    public MainViewModel()
    {
       ClicksCount = 0;
    }

    private int? _clicksCount;

    public int? ClicksCount
    {
       get => _clicksCount;
       set => SetProperty(ref _clicksCount, value);
    }

    private ICommand _increaseCount;

    public ICommand IncreaseCount => _increaseCount ?? (_increaseCount = new MvxCommand(Increase));

    private void Increase()
    {
       ClicksCount++;
    }
 }

W tym przypadku widok i view model nie wiedzą o sobie zupełnie nic – bo nie muszą!

Mvvm Cross to na prawdę kawał dobrego kodu jednakże w mojej ocenie jest to odejście od natywnego kodowania tzn. oczywiście nadal jest to aplikacja natywna jednak programista nie pracuje z czystym SDK Androidowym tylko z „nakładką” i przez to nie ma 100% kontroli nad tym co się dzieje pod maską.

Wszystkie trzy projekty wrzuciłem na mojego GitHuba.

Pjona!

Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s