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

Aplikacja tworzona w ramach konkursu „Daj się poznać 2016” oficjalnie dostępna w kanale produkcyjnym Google Play.

Siemanko.

Minęło sporo czasu od ostatniego posta, jednak absolutnie nie był to dla mnie czas leserowania. Po blisko 9ciu miesiącach aplikacja „Mobile Second Hand” wylądowała oficjalnie w sklepie. Możecie ją pobrać tutaj. Natomiast stronka informacyjna znajduje się tutaj. Traktuje to jako mój osobisty sukces bez względu na to jakim (i czy w ogóle) odbije się to echem. Być może jesteś tu pierwszy raz dlatego przypomnę, że jeszcze w marcu o developmencie na androida wiedziałem tylko tyle, że pisze się w Javie. Teraz mogę się pochwalić działającą aplikacją. Apka mobilna to właściwie tylko klient – cała robota dzieje się w aplikacji serwerowej, z której również jestem bardzo zadowolony. Niech się dzieje co chce. Ja się muszę w końcu wyspać ;).

Dzięki Maciek za super bodziec w postaci DSP.
Dzięki Koluniu za super logo – Adam Kuś – najdokładniejszy grafik w stolycy! 😉

Oczywiście kodzik, który commitowałem podczas blogowania konkursowego jest mocno nieaktualny. Po konkursie, i po decyzji o próbie doprowadzenia projektu do końca przestałem commitować publicznie.

Pjona!

Widok do prowadzenia rozmowy w czacie aplikacji Mobile Second Hand

Siemanko.

W niedzielę poszły pierwsze commit’y do repo związane z czatem, a dziś lecą kolejne z m.in. z nowym activity do prowadzenia rozmów real time (oczywiście  w deweloperskim wyglądzie jak to przystało na takie totalne bezguście jak ja ;))

IMAG0082

Bubble Chat

To wyrażenie przewija się w internetach i oczywiście wśród użytkowników fejsbukowego messenger’a. Wiadomości wyświetlane w formie bąbelków, dymków itp. jak zwał tak zwał. Moja aplikacja nie może być gorsza i musi nadążać za trendami 🙂 Sama implementacja jest bardzo prosta. Każda z wiadomości to osobny row view zamknięty w relative layout, a ten z kolei odpowiednio ostylowany w momencie bind’owania view holder’a na podstawie informacji czy autorem wiadomości jest użytkownik. Informacja tą zawarłem w modelu wiadomości przekazywanego do list adaptera.

Co nowego na serwerze

W momencie łączenia się usera z serwerem SignalR zapisuję sobie do cache’a (serwera) jego id wraz z id połączenia. W ten sposób będę łatwo mógł sprawdzić, który user jest online, a także pobrać jego id połączenia w przypadku potrzeby wysłania do niego wiadomości. Oczywiście w przypadku rozłączenia się usera aktualizuję listę użytkowników online w cache’u. Dlaczego cache? Bo szybki. Co będzie jak trzeba będzie zrestartować aplikację na serwerze i dane z cache’a znikną? Nic nie będzie bo tym samym użytkownicy zostaną rozłączeni i po restarcie będą się ponownie łączyli więc cache znowu się zapełni 🙂

Podsumowanie

Podsumowując to wiecie, że to już mój 11sty tydzień aktywnego rozwijania projektu i blogowania w konkursie #dajsiepoznac? Tym samym spełniłem już warunki konkursu, aby być branym pod uwagę przy wynikach choć te nie są najważniejsze bo największą frajdę podczas mojego uczestnictwa w konkursie sprawia mi nauka nowych rzeczy. Jestem zadowolony, że udaje mi się znaleźć czas, aby troszkę pokodować oraz popisać głupot w internetach, dlatego nie zamierzam na tym poście poprzestać i będę męczył Was dalej! 😉

Pjona!

Pierwsze kody związane z czatem już na guthub’ie.

Siemanko

W od kilku postów wiecie, że przymierzałem się do rozpoczęcia prac na czatem i ogólnie komunikacją między użytkownikami aplikacji mobilnej w czasie rzeczywistym. Dziś nastał ten dzień , że już pierwsze kody możesz zobaczyć w repo, a w tym poście pierwsze screen’y i zdjęcia z wymiany informacji pomiędzy klientem, a serwerem. W tym odcinku klientem jest nasz (mój i mojej narzeczonej) „pitbull” o imieniu Dosia 😉

Aplikacja mobilna klientem SignalR

W projekcie Xamarin napisałem w portable class library klienta SignalR dzięki czemu będę mógł go wykorzystać cross-platform’owo (może kiedyś)Z tego klienta korzystam w projekcie androidowym w nowo utworzonym serwisie, który będzie odpowiedzialny za komunikację z serwerem SignalR. Połączenie i komunikacja odbywa się w oddzielnym wątku przez co nie będzie ona wpływać na responsywność apki.

Serwer SignalR

W projekcie backend’u napisałem (zbyt dużo powiedziane bo to dopiero kilka linijek) serwer SignalR. Będzie on oczywiście odpowiedzialny za komunikację, ale chcę aby działo się w nim znacznie więcej. Mianowicie chcę zapisywać sobie informację o tym, który użytkownik jest online, zapisywać do bazy konwersacje między użytkownikami i wiele innych. Nad tym będę pracował w najbliższym czasie. Jak na razie serwer SignalR tylko odbiera jedną określoną wiadomość i na nią odpowiada.

Pierwsza komunikacja real time

Odpalając aplikację mobilną wysyłam wiadomość do serwera SignalR:

a1

Screen serwera SignalR w trybie debug

w odpowiedzi zwracam wiadomość, którą obsługuję w aplikacji poprzez odpalenie tzw. notification – czyli powiadomienia:

2

IMAG0058

Po kliknięciu powiadomienia przeniosę użytkownika na widok wiadomości (którego jeszcze nie ma). Na tę chwilę przenoszę go na listę ogłoszeń, ale to jest teraz nieistotne bo najważniejszym było obsłużenie komunikacji.

Serwis android’owy korzystający z klienta SignalR działa w tle, także mimo iż aplikacja nie jest odpalona (tzn. nie ma jej na ekranie, ale jej proces działa) wiadomości są odbierane.

IMAG0061

3

IMAG0071

W tym momencie kliknięcie powiadomienia przeniesie użytkownika do aplikacji. Normalnie jak w messengerze! 😉

Podsumowanie

Trochę się obawiałem, ze będzie to trudny task i zajmie mi to trochę czasu zanim cokolwiek zacznie działać,  a ku mojemu zaskoczeniu SignalR jest na tyle zajebisty, że wystarczy tylko kilka linijek aby już zacząć komunikację. To już za mną dlatego teraz muszę ogarnąć cały tzw. workaround żeby w pełni zaimplementować funkcjonalność komunikacji między użytkownikami aby mogli bez problemów dogadać szczegóły dotyczące transakcji ;).

Pjona!

Kolejny widok w aplikacji Mobile Second Hand. Tym razem szczegóły ogłoszenia.

Siemanko.

Dziś króciutko o kolejnym ficzerze apki mobilnej – oczywiście w mocno deweloperskim stanie 😉 – przejście na szczegóły ogłoszenia oraz ich wyświetlenie.

Wejście w szczegóły po naciśnięciu elementu na liście

Pokazywałem już we wcześniejszych postach jak wygląda lista ogłoszeń pobranych z serwera, ale dla przypomnienia:

13177151_1011632882250440_2934029842522300517_n

To jest lista okrojonych wersji ogłoszeń, którą można sobie scrollować od góry do dołu i odwrotnie 😉 Jeśli któreś ogłoszenie jest szczególnie interesujące wystarczy w nie kliknąć aby przejść do jego szczegółów.

13177393_1011632665583795_5778124407927892321_n

Widok jest oczywiście scrollowalny dlatego szczegółów mogę tam zamieścić więcej jeśli przyjdą mi jakieś do głowy.

Co jest na szczegółach

Pewnie powiesz, że nie wiele więcej niż na liście 😉 Tak, w tej chwili tak, ale wynika to z faktu, że na tę chwilę nie główkuję co by się tu jeszcze przydało, a nie z ograniczeń technologicznych. Wracając do nagłówka:

  • odległość do sprzedającego – jest też na liście
  • informacja o stanie sprzedającego online/offline  – to będzie powiązane z chatem (nad którym zaczynam pracować)
  • informacja o tym czy sprzedającego interere tylko sprzedaż rzeczy czy jest tez zainteresowany wymianką – nie ma na liście
  • button do nawiązania kontaktu – związane z chatem i komunikacją real-time (nad którym zaczynam pracować). Przeniesie na widok wiadomości
  • zdjęcie – będzie większe i po kliknięciu wypełni ekran
  • tytuł – jest też na liście
  • dokładny opis – nie ma na liście
  • button do zobaczenia innych ogłoszeń autora tego ogłoszenia – przeniesie na listę ogłoszeń tylko tego sprzedającego (prawie jak na allegro ;))

Jak się to odbywa

W momencie kliknięcia elementu listy rzucam zdarzenie, którego argumentem jest Id danego ogłoszenia. Przy obsłudze zdarzenia startuje nowe Activity, do którego przekazuje to Id i w nim pobieram z serwera model z danymi dla tego ogłoszenia.

Podsumowanie

Jako tako to wygląda i nawet działa 😉 Teraz jak wspomniałem zaczynam bawić się z chatem i powiadomieniami. To ma być jeden z głównych ficzerów aplikacji. W niedzielę  o pierwszych kodach z tym związanych.

Pjona!

Dodawanie nowego ogłoszenia w Mobile Second Hand

Siemanko

Widziałeś/aś już jak się będzie można w apce mobilnej zalogować do systemu (tak, do systemu bo aplikacja mobilna w projekcie to tylko klient), jak wygląda teraz (mocno dewelopersko) lista ogłoszeń więc teraz czas pokazać jak wygląda i działa na tę chwilę tworzenie nowego ogłoszenia wraz z dodaniem go do systemu.

IMAG0037

Tworzenie nowego ogłoszenia w aplikacji mobilnej

Na powyższym zdjęciu widzisz listę ogłoszeń. W prawym dolnym rogu jest widoczny tzw. FAB. Przycisk który przy przewijaniu listy jest cały czas w tym samym miejscu (btw. można zrobić tak aby przy przewijaniu w dół znikał i pojawiał się po powrocie na początek listy, ale nie o tym). Pewnie się domyślasz do czego służy? 🙂 Po kliknięciu przechodzimy do tworzenia nowego ogłoszonka.

IMAG0038

Tadam. Oto formularz do nowego ogłoszenia. Elementy (kontrolki) do wprowadzania danych umieściłem na scroll’owalnym layoucie, dzięki czemu nie ogranicza mnie wysokość ekranu oraz intuicyjnie przesuwając w dół użytkownik przejdzie do końca formularza.

IMAG0039

Kolejny babol w designerze Xamarin’a

Wspominałem już w wcześniejszych postach o nierenderujących się komponentach w designerze (np. guzik do logowania z facebookiem czy FAB). Mam kolejnego babola. W designerze nie podejrzysz kontrolek, które nie mieszczą się na widoku, a są faktycznie na layoucie (mimo iż przesuniesz pasek scrolla)

1.jpg

Także zostaje tylko xml i „kodowe” dodawanie i ustawianie widoków. Oczywiście po starcie apki na telefonie widok jest scroll’owalny.

Pola formularza

Które pole do czego to chyba się orientujesz z opisów 🙂 Pokrótce:

  • tytuł główny, który będzie widoczny na liście ogłoszeń
  • dodatkowe informacje gdzie można się rozpisać, a te będą widoczne na detalu ogłoszenia (po wejściu na niego z listy)
  • cena bez częsśi dziesiętnych
  • pola radio do określenia jaka forma pozbycia się ciucha interesuje użytkownika, a ta z kolei będzie umieszczona jako jakaś ikonka na liście ogłoszeń
  • zdjęcie – oczywiście robione z poziomu aplikacji. Na razie tylko jedno, ale w kodzie zostawiłem sobie furtki aby można było robić i dołączyć kilka zdjęć
  • znów pola radio, tym razem do określenia czy do ogłoszenia ma być dołączona lokalizacja aktualna czy domowa zapisana w ustawieniach (np. wystawiam ogłoszenie u koleżanki, ale zaraz wracam do domu 30 km i tam będę przesiadywał ;))
  • guzior do opublikowania ogłoszenia

W momencie naciśnięcia guzika do publikacja sprawdzam czy wszystkie pola zostały wypełnione oraz czy zostało zrobione zdjęcie. Jeśli tak to idą requesty do serwera. Najpierw jeden ze zdjęciem, a po udanym zapisie drugi z modelem. To jak to ogarniam na serwerze jest tematem na jeden z kolejnych wpisów.

Podsumowanie

W ramach podsumowania przejdziemy razem przez proces dodawania ogłoszenia 🙂

  • wypełniamy formularz i robimy zdjęcie

IMAG0047

IMAG0040

  • naciskamy guzik „OPUBLIKUJ” i czekamy sekundkę, aż się wszystko zapisze

IMAG0042

  • po wszystkim jesteśmy przeniesieni z powrotem na listę ogłoszeń, gdzie możemy podziwiać nasze nowoutworzone ogłoszenie i czekać na kontakt od chętnego kupującego (kontakt także będzie się odbywał w aplikacji) 🙂
IMAG0045

0 km ponieważ się nie ruszyłem 😉 Jednak gdybym odszedł i odświeżył listę zobaczyłbym ile dzieli mnie do miejsca, w którym opublikowałem ogłoszenie.

Pjona!

Xamarin’owa apka doprowadzona do stanu jaki prezentuje w Android Studio.

Siemanko.

Dziś króciutko.

W dniu dzisiejszym udało mi się doprowadzić aplikację mobilną do stanu jaki prezentowała w Android Studio w momencie podjęcia decyzji o przesiadce na Xamarin’a, więc nareszcie będę mógł jechać z nowymi tematami. Nie zliczałem czasu, ale szacuję, że zajęło mi to ok 20 godzin. Biorąc pod uwagę, że nie miałem wcześniej z Xamarin’em nic wspólnego to myślę, że to dobry wynik, a przyjemność jaką sprawia mi pisanie w nim jest o wiele większa niż w Android Studio. Ostatnie posty nie dotykały funkcjonalności rozwijanych w projekcie dlatego pozwolę sobie przypomnieć Ci co jest zrobione i za co zabieram się od jutra (dziś już dość kodzenia ;)).

Jest zrobione (zarówno po stronie aplikacji mobilnej jak i na serwerze):

  • autentykacja za pomocą tokena JWT (logowanie/rejestracja za pomocą formularza lub facebooka)
  • ustalanie lokalizacji w aplikacji mobilnej
  • pobieranie listy ogłoszeń na podstawie lokalizacji użytkownika uderzającego do API z uwzględnieniem zadeklarowanego przez niego obszaru z jakiego chce zobaczyć ogłoszenia (na podstawie zadeklarowanego promienia – wszystko wyliczam na serwerze)
  • tworzenie nowego ogłoszenia wraz ze zdjęciem i zapis do bazy na serwerze co skutkuje natychmiastowym pojawieniem się ogłoszenia w systemie

Co będzie robione:

  • predefiniowane hasła kluczowe dotyczące rodzaju ubrania (np. sukienka, bluzka itp.) oraz koloru, które będę wyłapywał na serwerze z tytułu i opisu ogłoszenia aby następnie je powiązać z sobą powiązać. W aplikacji nie będę wymagał wybierania ręcznego kategorii, itp. jak to jest np. na Allegro. Ma to być maksymalnie proste dlatego przypisywaniem haseł zajmie się system.
  • pobieranie listy ogłoszeń z zawężeniem do predefiniowanego hasła (np. tylko ogłoszenia ze spodniami)
  • powiadomienia w tle o pojawieniu się nowych ogłoszeń w zadeklarowanym przez usera obszarze
  • wyświetlanie szczegółów ogłoszenia po kliknięciu go na liście
  • komunikacja między użytkownikami systemu (kupujący – sprzedający)

Podsumowanie

Cieszę się, że będę już mógł pracować nad nowymi rzeczami, a nie odgrzewać stare kotlety ;). Jeśli Ty też się cieszysz moim szczęściem to daj feedback ;). A jeśli nie – to nie ;p

Pjona!