Przez ostatni tydzień głównym zajęciem przy aplikacji Codziennik było tworzenie mechanizmu edycji wpisów. Wszystko szło w dobrym kierunku. Gdy wszystkie mechanizmy odczytu i zapisu edytowanego wpisu teoretycznie działały zauważyłem problem.
Jak działa mechanizm edycji?
Przy naciśnięciu przycisku Edytuj pojawia się nowy widok nad obecnym:
Umożliwia on wprowadzenie zmian do odpowiedzi pierwotnie udzielonych. Po zakończonej edycji panel, który za nią odpowiada jest zdejmowany ze stosu:
Wszystko wygląda na łatwe. Z poziomu strony wyświetlającej wpis otwieramy nowy widok poleceniem Push i przekazujemy do niego wpis do edycji. Następnie użytkownik zapisuje zmiany. Aplikacja zdejmuje panel edycji ze stosu poleceniem Pop. Tym samym wracamy do strony wyświetlającej wpis. Tyle teorii. Nie powinno być problemu w zaimplementowaniu, prawda?
Problem
Otóż niestety pojawia się problem. Po zapisaniu zmian po edycji następuje powrót do strony wyświetlającej wpis. Widnieje tam jednak treść jeszcze sprzed edycji! Po prostu widok się nie odświeża. Po głębszym zastanowieniu wydaje się to logiczne. Treść do panelu zostaje wczytywana w konstruktorze, a więc przy tworzeniu instancji widoku. Wykonując akcję Pop nie tworzymy nowego obiektu, tylko zdejmujemy obecny widok i tylko wracamy do poprzedniego. Nic nie zostaje zmienione. Działa to trochę na zasadzie zamykania karty w przeglądarce, po której następuje powrót do karty poprzedniej. Generalnie ta karta się nie odświeża.
Po chwili researchu w Internecie, okazało się, że rozwiązanie sprawy wcale nie jest takie proste. Należy podłączać się pod pewne akcje wykonywane przez widoki w momencie ich pojawiania się i znikania. Tu przyszedł mi do głowy pomysł. Może wczytywać dane nie przy tworzeniu instancji widoku, ale przy jego pojawianiu.
Wczytywanie danych przy pojawianiu się widoku
Metoda OnAppearing()
wywoływana jest za każdym razem, gdy widok pojawia się na ekranie. Wtedy gdy jest tworzony, ale i wtedy, gdy pojawia się po zdjęciu innego widoku ze stosu.
Postanowiłem to wykorzystać. Dane aplikacji są zapisywane do pliku. Gdy użytkownik zakończy edycję wpisu, ten jest zapisywany do pamięci urządzenia. Wystarczy więc odczytać właśnie wyedytowany wpis i jego treść załadować do widoku.
Przysłanianie OnAppearing()
Postanowiłem więc przesłonić metodę OnAppearnig()
i tam zaimplementować wczytywanie danych. Jednak wczytywanie danych w tej metodzie przeprowadzane jest tylko po edycji wpisu. Program rozpoznaje to po fladze edited, która ustawiana jest na wartość true przy naciśnięciu przycisku edycji na górnym pasku.
protected override void OnAppearing() { base.OnAppearing(); if(edited) { var ReadedEntry = EntryDataStorage.GetOneEntry(passedEntry); MakeLayoutAndLoadData(ReadedEntry); } }
Na początku należy wywołać tą samą metodę którą przysłaniamy. Później sprawdzam flagę edited. Jeżeli wpis był edytowany, najpierw wczytuję edytowany wpis za pomocą EntryDataStorage.GetOneEntry(passedEntry). passedEntry to jest wpis, z którym został wywołany widok wyświetlający wpis na początku(jeszcze przed edycją). Następnie wywoływana jest metoda MakeLayoutAndLoadData(ReadedEntry)
, która odpowiada za podpięcie wszystkich kontrolek do zawartości widoku i wczytanie do nich danych.
Co ważne ta metoda zostaje także wywoływana w konstruktorze, aby wczytać dane przy przejściu z listy wpisów do podglądu konkretnego wpisu. Aby nie duplikować kontrolek zawartość widoku jest „czyszczona”, jeśli coś się w niej znajduje. Metoda MakeLayoutAndLoadData
jest bardzo podobna do konstruktora, który prezentowałem we wpisie o dynamicznym generowaniu UI.
Długo myślałem, czy nie dałoby się rozwiązać tego problemu lepiej. Przedstawione powyżej rozwiązanie jest moim zdaniem najlepsze i najprostsze. Jeśli znasz lepsze/bardziej wydajne proszę daj mi znać w komentarzu.
Jak zawsze kod źródłowy można znaleźć na moim Githubie w tym repozytorium. Ta wersja została oznaczona tagiem 04-reread-data-on-pop. Przechodząc pod niego można zobaczyć jak wyglądał kod podczas pisania tego posta.