We wpisie o pierwszej wersji aplikacji Codziennik wymieniłem funkcje, które chciałbym jeszcze zaimplementować do końca trwania konkursu Daj się poznać 2017. Wczoraj już druga pozycja z tej listy(po różnych wersjach językowych) została przez mnie wdrożona. Dzisiaj będzie mowa o wyszukiwaniu z listy po dacie.
W Xamarin Forms istnieje kontrolka do wyszukiwania. Problem polega na tym, że umożliwia ona wyszukiwanie tekstu. Jednak w Codzienniku chodziło mi o wyszukiwanie wpisów po dacie, a nie po treści. Trzeba więc wymyślić rozwiązanie samemu. Okazuje się to nie takie trudne, dzięki wbudowanej w Xamarin.Forms kontrolce DatePicker oraz dzięki LINQ. Ale po kolei.
Kontrolka DatePicker
Na początku musimy dodać kontrolkę DatePicker. Ja dodatkowo ustawiłem, żeby wybrana na początku data, była dniem dzisiejszym. Można to zrobić za pomocą ustawienia odpowiedniego propertisa: Date = DateTime.Now
.
Nie chciałem, żeby użytkownik musiał sam wciskać przycisk Szukaj żeby wyświetliły się wpisy o danej dacie. Postanowiłem, że po każdej zmianie daty w kontrolce DatePicker wyszukiwane będą pasujące wpisy. Można to uczynić za pomocą akcji: DateSelected
.
Wyszukiwanie
Sam proces wyszukiwania odbywa się na obiekcie typu List<>. Możemy więc skorzystać z dobrodziejstw LINQ. Do przeszukiwania użyłem metody FindAll
. Musimy użyć predykatu, który ustali pasujące wpisy. Najłatwiej stworzyć jest wyrażenie lambda. W moim przypadku to wyrażenie wygląda tak:
x => x.Date.Date.Equals(datePicker.Date.Date)
Pewnie zastanawiasz się po co tak namnożyłem Date
. Otóż, model Entry w aplikacji zawiera propertis DateTime
o nazwie Date
. Jednak zawiera on też godzinę, której nie chcemy porównywać. Bazujemy tylko na dacie. Musimy więc ze struktury DateTime wyłuskać samą datę. Robimy to właśnie za pomocą wywołania propertisa o nazwie Date.
Metoda FindAll
zwraca nam listę wyszukanych elementów, które pasowały do naszego predykata. Uznałem, że w przypadku, gdy nie zostanie znaleziony żaden wpis o danej dacie, aplikacja będzie wyświetlała napis powiadamiający o tym fakcie.
Dopiero w przypadku, gdy zostaną wyszukane jakieś wpisy, aplikacja będzie wyświetlała ListView
z nimi.
Ponowne wyszukiwanie
Gdy tylko napisałem całą powyższą funkcję, zadowolony z siebie zacząłem ją testować. Szybko okazało się, że jeśli użytkownik,, będzie chciał przeprowadzić więcej niż jedno wyszukiwanie, kolejne wyniki będą się dopisywały na końcu, a nie wyświetlały zamiast poprzednich. Po chwili zastanowienia wpadłem na poniższe rozwiązanie:
if (layout.Children.Count > 2) layout.Children.RemoveAt(2); if(layout.Children.Count > 1) layout.Children.RemoveAt(1);
Kod ten sprawdza, czy istnieje więcej niż 2 elementy w danym layoucie(DatePicker, Label, ListView) i usuwa ten ostatni. Następnie sprawdzane jest czy istnieje więcej niż 1 element(DatePicker i Label). Jeśli tak, to usuwany jest Label. Tym sposobem zostaje tylko DatePicker.
Dlaczego przed usunięciem sprawdzane są warunki? Otóż, nie zawsze będą 3, czy nawet 2 elementy. Przy wejściu do tego widoku znajduje się w nim tylko DatePicker. Wtedy żaden z warunków nie przejdzie przy wyszukiwaniu. Przy braku wpisów o podanej dacie w layoucie pojawia się Label
. Po ponownym wyszukaniu, przy braku wpisów zajdzie tylko drugi warunek i zostanie usunięta kontrolka o numerze 1.
Natomiast, gdy ponowne wyszukanie nastąpi po znalezieniu listy wpisów, oba warunki zostaną spełnione. Wtedy Label
i ListView
zostaną usunięte.
Nie jest to rozwiązanie eleganckie, ale działa. Jeśli wiesz jak rozwiązać ten problem, proszę daj mi znać w komentarzach.
Tym samym z funkcji, które chciałbym jeszcze dodać do Codziennika została tylko synchronizacja wpisów z chmurą i eksport wpisów. Dwie rzeczy, które wymagają większego przygotowania i dużej pracy. Jednak został jeszcze miesiąc do końca konkursu. Mam nadzieję, że do tego czasu uda mi się chociaż dodać funkcję synchronizacji wpisów.
Jak zawsze kod źródłowy można znaleźć na moim Githubie w tym repozytorium. Ta wersja została oznaczona tagiem 07-search. Przechodząc pod niego można zobaczyć jak wyglądał kod aplikacji Codziennik podczas pisania tego posta.
Nie znam się na Xamarinie, ale fajnie widać, jak robisz postęp mimo prostoty aplikacji. Podoba mi się pomysł na tagowanie w repozytorium. Powodzenia dalej! 🙂
Co do usuwania elementu – znam XAML z WPF-a, nie dałoby się nadać elementom ListView i Label nazw (Name) i w ten sposób się do nich odwoływać? Bo tak według indeksu to tak jak sam zauważyłeś – mało elegancko i wystarczy rozbudować widok, żeby przestało działać zgodnie z planem. Może wtedy dałoby się dla ListView podmieniać tylko kolekcję źródłową, a dla Labela tekst zależnie od okoliczności?
Dzięki za miłe słowa 🙂 Co do propozycji – oczywiście że się da! Jakbym zupełnie o tym nie wiedział. Jednak jak ktoś spojrzy świeżym okiem na problem to często od razu znajdzie rozwiązanie. Dzięki 🙂