Zmiany

Lista zadań

Dodane 62 157 bajtów, 17:35, 8 paź 2015
Utworzono nową stronę "W tym projekcie stworzymy aplikację wyświetlającą zadania do wykonania oraz dodamy możliwość dodawania nowych zadań. Utworzymy klasę, która będzie przechowywa..."
W tym projekcie stworzymy aplikację wyświetlającą zadania do wykonania oraz dodamy możliwość dodawania nowych zadań. Utworzymy klasę, która będzie przechowywać zadania wraz z datą. Dodamy kontrolki, które wyświetlą zadanie oraz przyciski, które pozwolą nam na poruszanie się po liście zadań. Stworzymy nowe okno, w którym zapytamy o zadanie. Dodamy także walidację na polu daty.<br />
Umiejętności, które są potrzebne do wykonania tego ćwiczenia to:<br />
# znajomość środowiska '''Greenfoot'''
# znajomość Javy na poziomie tworzenia klas, tworzenia obiektów, pętli oraz instrukcji warunkowych if, wiedza czym jest metoda, pole i zmienna lokalna
# umiejętność posługiwania się dokumentacją w Greenfoot
# podstawowa znajomość elementów interfejsu użytkownika (GUI), czym jest pole tekstowe, etykieta, okno dialogowe, przycisk

'''Zaczynajmy!'''

== Wstęp ==
Zadanie, które wykonamy w tym ćwiczeniu pozwoli Ci zapoznać się z elementami graficznego interfejsu użytkownika. Mówiąc inaczej ze wszystkimi rodzajami elementów, których używamy korzystając z różnych aplikacji. Są to m.in. pola tekstowe, listy rozwijane, menu, przyciski. <br />
Na przykładzie naszego ulubionego Greenfoot-a przejrzyjmy listę kontrolek, które zapewne znasz z innych aplikacji.<br />
Pole tekstowe
[[File:jmk-zadania_image136.png|center]]
Checkbox
[[File:jmk-zadania_image78.png|center]]
Lista wyboru
[[File:jmk-zadania_image02.png|center]]
Menu górne
[[File:jmk-zadania_image41.png|center]]
Menu kontekstowe
[[File:jmk-zadania_image109.png|center]]
Suwak
[[File:jmk-zadania_image15.png|center]]
Przyciski
[[File:jmk-zadania_image47.png|center]]
To jednak tylko część elementów, każdy z nich ma inne właściwości i używa się ich tak, aby użytkownik aplikacji w jak najprostszy sposób mógł z niej korzystać.

== Zapoznanie z Greenfoot GUI Components ==
Greenfoot posiada rozszerzenie stworzone przez Taylora Borna, które pozwala na używanie takich elementów we własnej aplikacji. Taką aplikację stworzymy właśnie w tym ćwiczeniu. Na początek przyjrzyjmy się oferowanym przez Taylora kontrolkom.<br />
Wyszukaj w google frazy “Greenfoot GUI compoenents” lub wejdź na poniższy link [[http://www.greenfoot.org/scenarios/7578|http://www.greenfoot.org/scenarios/7578]]. Powinieneś zobaczyć opis rozszerzenia jak na poniższym obrazku.
[[File:jmk-zadania_image58.png|center]]
Kliknij '''Open in Greenfoot'''. Zostanie pobrany plik o rozszerzeniu '''gfar''', który możesz otworzyć w '''Greenfoot'''. Kliknij po prostu dwukrotnie na plik lub przez menu '''Greenfoot''' otwórz go wybierając kolejno '''Scenario -> Open''', i wskaż na pobrany plik.<br />
Po otwarciu powinieneś zobaczyć okno Greenfoot takie, jak na obrazku poniżej.
[[File:jmk-zadania_image101.png|center]]
Teraz skompilujmy ten program. '''Uwaga!''' Ze względu na to, że w Greenfoocie, który otrzymaliście od nas, już dodaliśmy te klasy, pojawią się ostrzeżenia (kilkanaście), jak na poniższym obrazku. Kliknij po prostu przy każdym '''OK.'''
[[File:jmk-zadania_image16.png|center]]
Po zamknięciu wszystkich wiadomości projekt się skompiluje i Twoim oczom powinno ukazać się okno takie, jak poniżej.
[[File:jmk-zadania_image44.png|center]]
Poklikaj w aplikacji, zobacz jak działają poszczególne elementy GUI.<br />
Jak możesz zauważyć, świat nazywa się '''GUI_DemoWorld'''. Świat, jak w każdej aplikacji, pozwala na dodawanie aktorów. W tym przypadku to kontrolki są aktorami.<br />
Przeanalizujmy strukturę kontrolek.<br />
# Wszystkie z nich dziedziczą z klasy '''GUI_Component''', jest to klasa bazowa dla wszystkich kontrolek. Jeśli zajrzysz w jej dokumentację, dowiesz się jakie ma metody i na co pozwalają wszystkie kontrolki. Klasa ta pozwala m.in. na zmianę tła, czcionki, obramowania. Oznacza to, że wszystkie dziedziczące po niej kontrolki mają te metody i możesz ich używać.
# Następna klasa to '''WindowComponent'''. Jest klasą bazową dla wszystkich podstawowych elementów takich jak lista rozwijana (DropDownList), pole tekstowe (TextBox), przycisk (Button), hasło (Password), itd.
# Kolejna klasa bazowa to '''Window'''. Jest to klasa bazowa, z której dziedziczą klasy odpowiedzialne za wyświetlanie wyskakujących okien. Jeśli w przykładowej aplikacji wybierzesz '''Window Examples''' i któryś z elementów podmenu, to zobaczysz nowe okno.
<big>'''Zapoznanie z klasą GUI_DemoWorld'''</big>

W pierwszej kolejności przed przystąpieniem do stworzenia nowego projektu spójrzmy jak wygląda kod klasy '''GUI_DemoWorld.''' Pozwoli to na zapoznanie się z tym, jak autor widział wykorzystanie napisanych przez niego klas. Podczas przeglądania kodu zwróć szczególną uwagę na elementu ekranu (przyciski, etykiety), jak nazywają się poszczególne klasy i jakie metody posiadają.<br />
Otwórz klasę klikając na nią dwukrotnie.
[[File:jmk-zadania_image144.png|center]]
Na początku klasy widać pola, które są kontrolkami. Umieszczono je tutaj, aby mogły być wykorzystane w całej klasie. Zobaczmy jak są inicjowane.
[[File:jmk-zadania_image09.png|center]]
W konstruktorze, czyli w momencie tworzenia świata, tworzone są obiekty, a ich wartości są przypisane do pól.
[[File:jmk-zadania_image74.png|center]]
Do pola txtB zostaje przypisane pole tekstowe, w które można wprowadzić własny tekst.<br />
Następnie za pomocą polecenia
[[File:jmk-zadania_image139.png|center]]
pole tekstowe '''txtB''' jest dodawane do świata, aby je wyświetlić. Obiekt ten dodaje się do świata tak samo jak wszystkich aktorów, gdyż właśnie '''TextBox''' jest dzieckiem klasy '''Actor'''.<br />
Jeśli klikasz na przyciski, możesz zauważyć, że wywołują one pewne akcje. Zobaczmy jeszcze jak to jest obsłużone. W metodzie '''act()''' zobaczysz taki oto kawałek kodu:
[[File:jmk-zadania_image121.png|center]]
W pierwszej instrukcji sprawdzane jest, czy przycisk '''btnClick''' został kliknięty. Jeśli tak, to w następnym kroku sprawdzana jest aktualna akcja.
[[File:jmk-zadania_image114.png|center]]
Jeśli akcja ma polegać na dodaniu do licznika, to za pomocą takiej instrukcji w wyświetlanej etykiecie (label), wartość zostaje zwiększona o 1. Wartość tutaj jest pobierana z pola tekstowego o nazwie '''lblCounter''' za pomocą metody '''getText'''. Następnie za pomocą wywołania metody z klasy '''Integer.parseInt''' napis zostanie zamieniony na liczbę. Dopiero teraz zadziała dodawanie.
[[File:jmk-zadania_image30.png|center]]
{| class="wikitable" border="1"
|''Uwaga! Projekt GUI_Components warto jest mieć otwarty, aby móc się na nim wzorować. Greenfoot może być uruchomiony w kilku oknach.''
|}

=== Założenie projektu ===
W pierwszym kroku załóżmy nowy projekt w Greenfoot. '''Pamiętaj, aby użyć Greenfoota dostarczonego przez nas.'''<br />
Wybierz z menu '''Scenario -> New''' i załóż projekt, nazwij go '''Zadania'''. Pojawi się okno jak poniżej.
[[File:jmk-zadania_image31.png|center]]

=== Stworzenie świata ===
W tym kroku utwórzmy klasę świata. Na niej będziemy pokazywać odpowiednie kontrolki. '''Kliknij PPM''' na klasie '''World''' i wybierz '''New subclass'''. W nowym oknie wpisz nazwę klasy '''ListaZadan'''.
[[File:jmk-zadania_image140.png|center]]
Skompiluj projekt. Powinieneś zobaczyć czysty ekran.
[[File:jmk-zadania_image81.png|center]]
Otwórz do edycji tę nowo utworzoną klasę. Dodamy do niej etykiety i pola tekstowe, na których później wyświetlimy zadania.<br />
Zastanówmy się, co jest potrzebne aby pokazać zadanie. Jakie elementy interfejsu użytkownika mogą być nam potrzebne?<br />
Potrzebne będzie nam na pewno pole tekstowe do wyświetlania daty i opisu zadania. Warto dodać też etykiety, które te pola nazwą, tak aby od razu było jasne które odpowiada za wyświetlanie jakiej informacji.<br />
Przejdźmy do klasy i stwórzmy te elementy. Najpierw dodajmy etykietę ('''Label''') i '''TextBox''' z opisem zadania. Opis może być duży, więc zróbmy trochę większe pole tekstowe.<br />
Dodaj pole '''opisBox''' pod definicją klasy '''ListaZadan'''. Będziemy mogli z niego korzystać we wszystkich metodach danej klasy.<br />
''<small>Pola można zdefiniować w dowolnym miejscu klasy (oczywiście nie w metodzie i konstruktorach), jednak ze względu na czytelność najlepiej umieszczać je na samej górze, tuż po definicji klasy a przed konstruktorami i metodami.</small>''
[[File:jmk-zadania_image38.png|center]]
Teraz stwórzmy nową metodę w tej klasie, która doda nam etykietę i zainicjuje pole tekstowe. Nowa metoda pozwoli nam lepiej zorganizować kod, zrobić go bardziej czytelnym. Warto aby klasa składała się z małych metod, które są czytelne i posiadają czytelne nazwy. Naszą metodę nazwijmy '''inicujPodgladZadania'''. Wykorzystamy tutaj klasę '''Point''' z pakietu '''java.awt'''. Z tego względu na górze pliku ListaZadan.java musimy dodać odpowiedni import. Pamiętaj o pozostawieniu starego importu, będzie on nam cały czas potrzebny, gdyż używamy między innymi klasy '''World''', która pochodzi z pakietu '''greenfoot'''.
<small>Klasa '''Point''' jest szeroko wykorzystywana w tym scenariuszu. Z założenia miała określać punkt na osi współrzędnych, jednak w tym wypadku będzie oznaczać wielkość obiektu. Konstruktor przyjmuje dwie wartości, pierwsza to szerokość, druga to wysokość. new '''TextBox(new Point(160, 60)''' - będzie oznaczać, że ma zostać stworzone pole tekstowe o rozmiarze 160 pikseli na 60 pikseli</small>
[[File:jmk-zadania_image113.png|center]]
W metodzie stwórz etykietę, za pomocą konstruktora '''Label(trescOpisu)'''. Następnie stwórz pole tekstowe za pomocą konstruktora '''TextBox(rozmiar, tekst)'''. Za pomocą '''setReadOnly''' możesz ustawić, żeby pole było tylko do odczytu. Następnie wywołaj metodę '''addObject(element, x, y)''', która doda obiekt na świat w punkcie (x,y). Obiekt będzie miał rozmiar zgodny z tym co jest podane w konstruktorze za pomocą obiektu klasy '''Point'''.
<small>''Klasa Point jest szeroko wykorzystywana w tym scenariuszu. Z założenia miała określać punkt na osi współrzędnych, jednak w tym wypadku będzie oznaczać wielkość obiektu. Konstruktor przyjmuje dwie wartości, pierwsza to szerokość, druga to wysokość. '''new TextBox(new Point(160, 60)''' - będzie oznaczać, że ma zostać stworzone pole tekstowe o rozmiarze 160 pikseli na 60 pikseli''</small>
[[File:jmk-zadania_image08.png|center]]
Teraz wywołajmy tę metodę w bezparametrowym konstruktorze klasy '''ListaZadan''', czyli tam gdzie ją stworzyliśmy. Zauważ, że jest to metoda prywatna i nie można jej wywołać w innej klasie. Dodajmy też wywołanie '''Greenfoot.start()''', które uruchomi nam aplikację tuż po kompilacji.<br />
<small>''Konstruktor jest wywoływany podczas tworzenia obiektu, pisząc '''new Klasa(parametr)''', wywołujemy właśnie konstruktor, który utworzy obiekt. Klasa może mieć wiele konstruktorów, ale każdy z nich musi mieć różne parametry. Konstruktor może też nie mieć parametrów. Jeśli w klasie nie ma zdefiniowanego konstruktora, posiada domyślny konstruktor bez parametrów. Konstruktor parametrowy wykorzystujemy np. używając klasy Point, której obiekt tworzymy przekazując dwie wartości x i y. Definicja konstruktora wygląda w ten sposób '''public Point(int x, int y)'''.''</small>
[[File:jmk-zadania_image119.png|center]]
Skompiluj aplikację. Zobaczysz ekran z etykietą i polem tekstowym jak poniżej.
[[File:jmk-zadania_image92.png|center]]
{| class="wikitable" border="1"
|Zadania do wykonania:<br />
* zakomentuj linijkę uruchomiającą aplikację ('''Greenfoot.start'''), co się zmieniło?
* zmień wielkość pola tekstowego, jak to zrobić? (zmiana w konstruktorze '''TextBox''')
* zmień pozycje kontrolek, umieść je wedle uznania, pamiętaj że powinny się znaleźć obok siebie, tak aby wiadomo było jaka etykieta jest do jakiego pola
* co się stanie, gdy w wywołaniu '''opisBox.setReadOnly(true)''', zmienisz wartość '''true''' na '''false'''? Spróbuj kliknąć i wpisać coś w polu tekstowym
|}
Teraz w sposób analogiczny stwórzmy etykietę i pole tekstowe do terminu wykonania zadania. Dodaj pole w klasie '''ListaZadan'''.
[[File:jmk-zadania_image34.png|center]]
W metodzie '''inicjujPodgladZadania''' dodaj kod inicjujący etykietę i pole tekstowe.
[[File:jmk-zadania_image51.png|center]]
Uruchom aplikację, powinny pojawić się nowe kontrolki.
[[File:jmk-zadania_image71.png|center]]
{| class="wikitable" border="1"
|Zadania do wykonania:<br />
* za pomocą metody '''setBackgroundColor''' zmień kolor tła, do tego celu użyj klasy '''Color''', która posiada już zdefiniowane podstawowe kolory, dostępne można znaleźć pod adresem [[http://docs.oracle.com/javase/7/docs/api/java/awt/Color.html|http://docs.oracle.com/javase/7/docs/api/java/awt/Color.html]]
|}
[[File:jmk-zadania_image106.png|center]]
Uwaga! Musisz dodać w tej sytuacji import, który pozwoli na użycie klasy '''Color'''. Na górze klasy '''ListaZadan''' dodaj:
[[File:jmk-zadania_image05.png|center]]
* za pomocą metody setTextColor zmień kolor tekstu, sprawdź po kompilacji jak wygląda aplikacja
* zmień tło aplikacji za pomocą metody setBackground, wywołaj ją w konstruktorze klasy ListaZadan. Pamiętaj, że musisz dodać obrazek i podać jego nazwę

=== Klasa zadania ===
Mamy już podstawowe elementy GUI (ang. Graphical user interface) na których będziemy pokazywać aktualne zadania. Brakuje nam jednak klasy, która będzie reprezentować nasze zadanie. Na ekranie mamy dwie wartości, które są nam potrzebne w zadaniu: termin wykonania i opis. Pierwsza z nich będzie polem typu '''Date''', czyli klasy, która w Javie reprezentuje datę. Drugie pole będzie już znanym nam polem typu '''String''', czyli napisem.<br />

Klasę tworzymy przez wybranie w menu '''Edit -> New class'''. Podaj nazwę '''Zadanie'''. Jest to klasa pomocnicza, nie jest ona ani aktorem ani klasą świata. Odpowiada ona za przechowywanie danych. Następnie otwórz tę klasę, znajduje się w sekcji '''Other classes'''. Usuń zbędne pole '''x''' oraz metodę '''sampleMethod'''. Kod powinien wyglądać jak poniżej.
[[File:jmk-zadania_image104.png|center]]
Teraz musimy dodać potrzebne nam pola. Dodajmy jednak na górze pliku w pierwszej kolejności poniższy import.
[[File:jmk-zadania_image42.png|center]]
Następnie nad domyślnym (bezparametrowym) konstruktorem dodajmy pole z opisem i datą wykonania. Dzięki importowi możesz użyć klasę '''Date'''.
[[File:jmk-zadania_image48.png|center]]
Spróbuj skompilować program, jeśli wszystko napisałeś dobrze program skompiluje się prawidłowo i pojawi się okno aplikacji jak poprzednio. Jeśli pojawią się błędy, spróbuj je rozwiązać na podstawie komunikatu. Komunikaty są także widoczne w przypadku błędu kompilacji na dole klasy, w której pojawił się błąd.<br />
Teraz rozszerzmy klasę o konstruktor parametrowy. Będziemy mogli dzięki temu przekazać wszystkie potrzebne informacje tworząc nowy obiekt zadania.<br />
Zwróć uwagę, że nazwy parametrów konstruktora są takie same jak nazwy pól. Dlatego w konstruktorze piszemy '''this.opis''' i '''this.terminWykonania'''. Mówimy w ten sposób, że są to pola klasy. Instrukcja '''this.opis = opis''' oznacza, przypisz do pola '''opis''' tego obiektu zmienną (parametr) opis. Dla uproszczenia możesz zastosować inne nazwy parametrów, w takim wypadku nie pojawia się problem z tymi samymi nazwami zmiennych i nie musisz stosować słówka '''this'''.<br />
<small>''Stosowanie słówka this jest o tyle wygodne, że nie trzeba wymyślać nowej nazwy parametru.''</small>
[[File:jmk-zadania_image27.png|center]]
Dodajmy jeszcze po dwie metody, które pozwolą pobrać i ustawić wartość dla każdego z pól. Użyjemy tu nazewnictwa Javy. Metoda, która zwraca wartość poprzedzona jest przedrostkiem '''get''', zaś metoda ustawiająca wartość poprzedzona jest przedrostkiem set. Stwórz teraz '''getter''' i '''setter''' dla opisu i terminu wykonania. Zwróć uwagę, że nowe słowa oprócz pierwszego rozpoczynane są z dużej litery. Nie jest to konieczne, ale bardzo ułatwia czytanie kodu.<br />
<small>''Używanie metod do pobierania i zmienia pól w klasie jest dobrą praktyką, gdyż pozwala ukryć pewne rzeczy przed osobą korzystającą z tej klasy. Pozwala łatwiej sterować tym co można zmienić, a co tylko odczytać.''</small>
[[File:jmk-zadania_image147.png|center]]
Ponownie skompiluj aplikację. Staraj się robić to jak najczęściej. Za każdym razem gdy napiszesz jakąś pełną instrukcję. W takim wypadku dużo łatwiej jest znajdować błędy, które przytrafiają się zawsze i wszystkim bez wyjątku.<br />
To już tyle, jeśli chodzi o klasę zadania. Przechowa ona nam wszystko co nas w tym momencie interesuje.

=== Podgląd zadania w klasie świata. ===
Przejdźmy teraz do klasy '''ListaZadan''', na której będziemy operować w tej części zadania. Stwórzmy w niej nowe pole o typie '''Zadanie'''. Będzie przechowywać aktualnie wyświetlane zadanie
[[File:jmk-zadania_image72.png|center]]
Teraz zainicjujmy (przypiszmy wartość) wartość pola w konstruktorze '''ListaZadan'''.
[[File:jmk-zadania_image20.png|center]]
Skompiluj program, zobacz czy wszystko działa.<br />
Stwórzmy teraz metodę, która odświeży ekran, przypisując kontrolkom wartości z aktualnego zadania. Nazwijmy ją odswiezEkran. Dodaj ustawienie wartości do opisu zadania.
[[File:jmk-zadania_image50.png|center]]
Wywołaj teraz tę metodę w konstruktorze klasy, tuż przed uruchomieniem aplikacji.
[[File:jmk-zadania_image14.png|center]]
Skompiluj program. Sprawdź czy rzeczywiście w polu tekstowym opisu zadania, pojawia się spodziewana wartość “Pierwsze zadanie”. Jak na ekranie poniżej.
[[File:jmk-zadania_image133.png|center]]
Teraz dodajmy w metodzie '''odswiezEkran''' ustawianie wartości w polu terminu wykonania.
[[File:jmk-zadania_image28.png|center]]
Skompiluj program i zobacz co się stanie. Powyższa instrukcja wywołuje na dacie metodę '''toString''', która jest dostępna w każdej klasie Javy. Pozwala na wyświetlenie każdego obiektu w postaci napisu. Jednak efekt nie zawsze może być taki, jakiego się spodziewamy. Tak jak w tym przypadku.
[[File:jmk-zadania_image29.png|center]]
Zmieńmy wyświetlanie tak, aby było czytelne dla nas. W tym celu użyjemy klasy '''SimpleDateFormat''', która pozwoli nam łatwo poradzić sobie z wyświetlaniem daty. Najpierw dodajmy jeszcze jeden import '''java.text.'''* na górze pliku '''ListaZadan'''.
[[File:jmk-zadania_image62.png|center]]
Teraz dodaj kod jak poniżej. Tworzymy najpierw obiekt klasy '''SimpleDateFormat''' o nazwie '''formaterDawty'''. Podajemy w jego konstruktorze format w jakim chcemy wyświetlić datę. W naszym przypadku są to wartości:
* yyyy - czterocyfrowa wartość roku (y - od ang. year),
* MM - dwucyfrowa wartość miesiąca (M - od ang. month), uwaga muszą być dużymi literami, małe oznaczają minuty,
* dd - dwucyfrowa wartość dnia (d - od ang. day).

Bardziej szczegółowo jest to opisane w dokumentacji klasy dostępnej pod adresem [[http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html|http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html]].

Następnie wywołujemy metodę '''format''' na obiekcie '''formaterDaty'''. Do metody przekazujemy datę, którą chcemy wyświetlić. I tak dostajemy już interesujący nas napis. Ustawiamy napis w polu tekstowym terminu wykonania.
[[File:jmk-zadania_image06.png|center]]
Uruchom kod. Teraz wyświetlany termin powinien być czytelny.
[[File:jmk-zadania_image143.png|center]]

=== Przeglądanie listy zadań ===
W tym kroku skupimy się na stworzeniu listy zadań i przechodzeniu po niej. Na razie stworzymy listę na starcie aplikacji. Zastanówmy się jak najwygodniej będzie przechodzić użytkownikowi po kolejnych zadaniach.<br />
Możemy do tego celu wykorzystać np. dwa przyciski: następne zadanie i poprzednie zadanie. Zakodujmy to.<br />
Stwórzmy najpierw listę zadań. Potrzebny będzie nam import klasy '''java.util.ArrayList'''. W klasie '''ListaZadan''' dodaj na górze odpowiedni import.<br />
<small>''ArrayList to jeden z podstawowych typów Javy. ArrayList pozwala na przechowywanie dowolnego rodzaju danych w postaci listy. Lista charakteryzuje się tym, że wartości są w niej dodawane w kolejności, domyślnie na koniec listy. Elementy na liście są zdefiniowane w kolejności i jeśli nic nie zmieniamy, to możemy je łatwo odczytać. Sprawdźmy jakie metody ma lista i jak możemy jej używać. Należy też pamiętać, że podobnie jak z tablicami elementy są numerowane od zera. To oznacza, że po stworzeniu pustej listy i dodaniu elementu, posiadamy listę o rozmiarze 1, ale wartość pobieramy przez metodę z podaniem indeksu 0, np. lista.get(0). Analogicznie drugi element pobieramy przez podanie indeksu 1, lista.get(1).
[[https://docs.oracle.com/javase/7/docs/api/java/util/List.html| https://docs.oracle.com/javase/7/docs/api/java/util/List.html]]''</small>
[[File:jmk-zadania_image112.png|center]]
Teraz dodajmy pole z listą zadań, będzie ona przechowywać wszystkie zadania.
[[File:jmk-zadania_image100.png|center]]
Teraz musimy w konstruktorze dodać kod, który stworzy a następnie doda na listę kilka zadań, tak aby po stworzeniu świata lista nie była pusta. W konstruktorze '''ListaZadan''' dodaj na przykład taki kod.
[[File:jmk-zadania_image137.png|center]]
Jak może zauważyłeś obecnie lista nie jest w żaden sposób powiązana z aktualnym zadaniem. Musimy to poprawić.<br />
Do tego celu będzie nam potrzebne kolejne pole typu liczbowego '''int'''. Będzie ono przechowywać indeks (numer z listy) aktualnie wyświetlanego zadania. Będzie to nam potrzebne przy przechodzeniu po liście zadań. Dodaj to pole w klasie '''ListaZadan'''.
[[File:jmk-zadania_image57.png|center]]
Teraz poprawmy kod w konstruktorze, aby ustawiał indeks na 0, będzie to początkowa wartość, a następnie ustawiał aktualne zadania pobierając je z listy. Usuńmy poprzednie przypisanie zadania do pola aktualneZadanie.<br />
<small>'''Pobieranie elementów z listy możliwe jest za pomocą metody '''get(int index)''', która zwraca element spod podanego indeksu. Trzeba pamiętać, że numerowanie rozpoczyna się od zera. Jeśli mamy dwa elementy na liście, to pierwszy z nich pobieramy za pomocą wywołania '''lista.get(0)''', a drugi za pomocą '''lista.get(1)'''.'''</small>
[[File:jmk-zadania_image83.png|center]]
Uruchom aplikację. Wszystko powinno działać bez zmian. Powinno pojawiać się pierwsze zadanie z nowej listy. W celu przetestowania, czy to działa, ustaw chwilo wartość '''indeksAktualnegoZadani''' na 1 lub 2 i ponownie uruchom aplikację. Czy teraz wyświetlane jest inne zadanie?
[[File:jmk-zadania_image118.png|center]]
Świetnie, zmień wartość indeksu znów na 0.<br />
Dodajmy teraz przyciski do przechodzenia po liście. Będą to na obiekty klasy '''Button'''. Nie musisz dodawać importu, aby z niej skorzystać. Utwórz dwa pola '''poprzednieZadanie''' oraz '''nastepneZadanie'''.
[[File:jmk-zadania_image21.png|center]]
Teraz stwórzmy obiekty przycisków i ustawmy je na ekranie. Zróbmy to w nowej metodzie. Dodaj metodę '''inicujujPrzyciskiNawigacji''' w klasie '''ListaZadan''' i wprowadź kod inicjujący przyciski.
[[File:jmk-zadania_image11.png|center]]
Czy to już zadziała, czy przyciski się pojawią? Uruchom aplikację, aby się przekonać.<br />
Jak zapewne zauważyłeś, przyciski nie są widoczne. Czy wiesz czego brakuje? Musimy wywołać tę metodę w konstruktorze '''ListaZadan'''. Trzeba pamiętać, że deklaracja (kod powyżej) jest zupełnie czymś innym niż wywołanie metody (kod poniżej). Metoda nigdy nie będzie wywołana sama, programista musi wywołać ją jawnie na obiekcie.
[[File:jmk-zadania_image115.png|center]]
Uruchom teraz aplikację. Czy przyciski są widoczne? Pamiętaj, że możesz poeksperymentować z wyglądem, wielkością i położeniem przycisków.
[[File:jmk-zadania_image43.png|center]]
Świetnie, ale czy coś się stanie po kliknięciu na przycisk?<br />
Oczywiście, że nie. Musimy to zaprogramować.<br />
Wszystkie kliknięcia na przyciski jesteśmy w stanie obsłużyć w metodzie '''act'''. Musimy sprawdzić w niej czy przycisk został wciśnięty. Następnie zależnie od tego czy wciśnięto następny, czy poprzedni musimy zwiększyć lub zmniejszyć indeks aktualnego zadania. Zacznijmy od przechodzenia w przód, czyli sprawdzania czy naciśnięto '''nastepneZadanie'''.<br />
<small>''Act to jedna z najważniejszych metod w Greenfoot, w niej obsługiwane są akcje w danym punkcie działania aplikacji.''</small><br />
Najpierw dodaj nową metodę w klasie '''ListaZadan''': '''sluchajNaZmianieZadania''', w której umieścimy odpowiedni kod. Pamiętaj o dodaniu metody '''act''' i wywołaniu w niej metody '''sluchajNaZmianieZadania'''.
[[File:jmk-zadania_image91.png|center]]
Zwróć uwagę, że kolejność metod nie ma znaczenia. Możemy najpierw wywołać metodę, która jest zakodowana gdzieś poniżej.<br />
Przyjrzyj się metodzie '''sluchajNaZmianieZadania''', zwróć uwagę co się w niej dzieje. Najpierw sprawdzamy w za pomocą if czy wciśnięto przycisk. Metoda '''wasClicked()''' zwraca nam wartość '''true''' jeśli ktoś wcisną przycisk. W przeciwnym wypadku zwraca '''false''', wtedy nie musimy nic robić. Po wciśnięciu przycisku zwiększamy indeks o jeden.<br />
Uruchom aplikację i wciśnij przycisk “'''Następne zadanie'''”. Czy zadanie się zmieniło? Niestety nie, brakuje nam dwóch rzeczy. Czy wiesz jakich?<br />
Musimy przypisać nową wartość do pola '''aktualneZadanie''' oraz wywołać metodę odświeżającą ekran. Zróbmy to.
[[File:jmk-zadania_image69.png|center]]
Uruchom teraz aplikację. I kliknij ponownie kilkukrotnie na przycisk “'''Następne zadanie'''”.<br />
Na początku powinno wszystko zadziałać, ale jeśli klikniemy więcej razy niż jest zadań na liście pojawi się błąd. Powinieneś zobaczyć okno jak poniżej.
[[File:jmk-zadania_image17.png|center]]
Greenfoot pozwoli nawet przejść w miejsce wystąpienia błędu. Kliknij na pierwszą czerwoną linię. Zostaniesz przekierowany w miejsce gdzie jest błąd.
[[File:jmk-zadania_image61.png|center]]
<small>''Błąd pojawia się przy pobieraniu aktualnego zadania. Patrząc na komunikat o błędzie:<br />
Warto nieznane błędy skopiować i wrzucić do wyszukiwarki internetowej. Większość problemów została już rozwiązana i bardzo łatwo jest znaleźć opis rozwiązania na różnych stronach internetowych.''</small>
'''java.lang.IndexOutOfBoundsException: Index: 3, Size: 3'''<br />
Możemy się domyślić, że chodzi tutaj o próbę pobrania zadania spod indeksu 3. Jednak na liście mamy tylko trzy zadania, a próba pobrania zadania o numerze 3, tak naprawdę powoduje odwołanie do nieistniejącego, czwartego elementu listy. Musimy teraz zrobić pewne obejście.<br />
Dodajmy sprawdzanie czy nie przeszliśmy za daleko i w takim wypadku zacznijmy indeksowanie od początku. Czy wiesz jak to zrobić? Możesz skorzystać z metody '''size''', którą oferuje nam lista. Jak można się domyślić zwraca nam liczbę elementów.
[[File:jmk-zadania_image66.png|center]]
Uruchom ponownie aplikację. Kliknij kilka razy na przycisk i sprawdź jak działa aplikacja. Po przekroczeniu indeksu lista powinna iść od początku.<br />
Teraz spróbuj napisać podobny kod dla przycisku '''poprzednieZadanie'''. Pamiętaj, że teraz chcemy iść wstecz, czyli trzeba zmniejszać indeks aktualnego zadania.
[[File:jmk-zadania_image55.png|center]]
Uruchom aplikację czy wszystko działa. Jeśli tak to świetnie. Teraz będziemy mogli przejść dalej i dodawać swoje zadania do listy.

=== Dodawania nowego zadania ===
Teraz zastanówmy się jak możemy zrobić dodawanie nowego zadania. W większości aplikacji pojawia się nowe okno, które pozwala na wprowadzenie danych. Po ich wprowadzeniu nowe dane pojawiają się na liście.<br />
Spróbujmy to teraz zaimplementować. Zacznijmy od stworzenia nowego okna.<br />
Stwórz nową klasę '''OknoZadania'''. Stwórz ją wybierając z menu '''Edit -> New class'''. Podaj jej nazwę, następnie otwórz stworzoną klasę. Usuń jak poprzednio niepotrzebne pole '''x''' i przykładową metodę <br />
'''sampleMethod'''. Pozostaw konstruktor. Klasa powinna wyglądać jak poayList.<br />
<small>''W ten sposób tworzymy klasy, które nie są ani aktorami ani klasami świata. Mogą one służyć do np. przechowywania i pobierania informacji, komunikowania się z innymi systemami. Mogą być klasami pomocniczymi, które tworzą metody wykorzystywane przez inne klasy. Możesz patrzeć na takie klasy jak na te, używane przez nas, które są w Javie np. '''Date, ArrayList'''.''</small>
[[File:jmk-zadania_image129.png|center]]
Żeby można byłoby ją traktować jako wyskakujące okno musimy zmienić klasę, aby dziedziczyła z klasy '''Window'''. Pamiętaj, że klasę '''Window''' możesz podejrzeć w projekcie '''GuiComponents'''. Dodajmy też wywołanie konstruktora klasy Window w konstruktorze '''OknoZadania''', bez tego aplikacja nie może się skompilować, gdyż klasa Window nie ma bezparametrowego konstruktora. Robimy to przez wywołanie '''super'''(super pozwala na odwołanie się do dziedziczonej klasy z jej podklasy).<br />
<small>''Dziedziczenie możesz traktować jako rozszerzanie. Rozszerz klasę dodając do jej definicji extends i nazwę klasy, z której następuje dziedziczenie.''</small><br />
Przejdźmy do klasy '''ListaZadan''' i dodajmy tam nowe pole z przyciskiem i drugie pole z naszą klasą '''OknoZadania'''.
[[File:jmk-zadania_image90.png|center]]
Zainicjuj te pola w konstruktorze '''ListaZadan''' i dodaj przycisk na ekranie.
[[File:jmk-zadania_image54.png|center]]
Uruchom aplikację, zobacz czy pojawił się nowy przycisk.
[[File:jmk-zadania_image43.png|center]]
Jak może zauważyłeś, po jego kliknięciu nic się nie dzieje. Dodajmy odpowiednią metodę o nazwie '''sluchajNaDodaniuZadania''' w klasie '''ListaZadan''', którą wywołamy w metodzie '''act'''. Niech pokaże nam nowe okno wywołując metodę '''toogleShow''', po kliknięciu przycisku. '''ToogleShow''' pokazuje okno, gdy jest ukryte lub ukrywa, gdy okno jest widoczne.
[[File:jmk-zadania_image84.png|center]]
Wywołanie w metodzie '''act'''.
[[File:jmk-zadania_image77.png|center]]
Uruchom aplikację. Po kliknięciu na “Dodaj zadanie” powinno pojawić się mało wyraźne okno z przyciskiem x.
[[File:jmk-zadania_image131.png|center]]
Jak widać okno nie jest jeszcze zbyt przydatne. Aby dodać zadanie potrzebujemy kliku rzeczy. Zastanówmy się jakich. Na pewno są to pola tekstowe. Etykiety do ich opisu. Przydadzą się też dwa przyciski: dodaj i zamknij.<br />
Przyciski i pola tekstowe dodajmy jako pola klasy '''OknoZadania''', gdyż będą nam później potrzebne w różnych metodach.
[[File:jmk-zadania_image79.png|center]]
Oczywiście musimy te pola zainicjować i dodać do okna, podobnie jak robiliśmy to dla świata, w konstruktorze '''OknoZadania'''.<br />
Ponownie musimy dodać na górze pliku '''OknoZadania''' import klasy '''java.awt.Point''', aby móc tworzyć obiekty klasy '''Point''', które wykorzystamy do tworzenia kontrolek.
[[File:jmk-zadania_image99.png|center]]
Niestety przy pracy z oknem musimy korzystać z kontenerów. Są to takie obiekty, które można traktować jak tabelki. W tabelki wkładamy odpowiednie elementy GUI jak przycisk czy etykieta.<br />
W naszym przypadku potrzebujemy głównej tabeli, który będzie miał wymiar 3x1 (3 wiersze, 1 kolumna), oraz trzech tabelek o wymiarach 1x2 ( 1 wiersz, 2 kolumny). W te trzy tabelki dodamy odpowiednio etykietę i pole dla opisu, etykietę i pole dla terminu oraz w ostatnim dwa przyciski: dodaj i zamknij.<br />
Zacznijmy od zdefiniowania w konstruktorze głównej tabeli (kontenera), etykiety i pola opisu, dodaniach ich do własnej tabeli o rozmiarze 2x1. Na końcu dołączymy główną tabelę do okna.
[[File:jmk-zadania_image25.png|center]]
Teraz uruchom aplikację, po kliknięciu powinien już zobaczyć pole tekstowe do opisu.
[[File:jmk-zadania_image148.png|center]]
Teraz w konstruktorze '''OknaZadania''' dodaj podobny kod dla terminu wykonania.
[[File:jmk-zadania_image105.png|center]]
Ponownie uruchom aplikację i sprawdź czy pojawiły się kolejne pola w oknie dodawania zadania.<br />
Spróbuj wpisać różne wartości w pole terminu wykonania. Jak może zauważyłeś możesz ich wpisać różne znaki. Jednak data składa się tylko z cyfr i myślników oraz ma długość 10 znaków. Dodajmy takie ograniczenie na polu '''terminuWykonaniaBox'''. W konstruktorze po utworzeniu pola '''terminWykonaniaBox''' wywołaj metodę '''acceptOnly''', w której w postaci napisu możesz przekazać dozwolone wartości. Wywołaj też metodę setMaxLength, do której przekażesz maksymalną długość tekstu. Greenfoot ma bardzo przydatną funkcję podpowiadania nazw. Wystarczy, że wpiszesz “'''terminWykonaniaBox'''.” i naciśniesz '''Ctrl+Spacja''', wtedy pojawią się podpowiedzi z nazwami pól i metod.
[[File:jmk-zadania_image59.png|center]]
[[File:jmk-zadania_image65.png|center]]
Uruchom aplikację i zobacz jakie wartości możesz teraz wpisać w pole termin wykonania.<br />
Jeśli wszystko działa prawidłowo dodajmy jeszcze przyciski dodawania i zamykania okna w konstruktorze '''OknoZadania'''.
[[File:jmk-zadania_image39.png|center]]
Uruchom po raz kolejny aplikację, zobacz czy pojawiły się nowe przyciski.
[[File:jmk-zadania_image22.png|center]]
Spróbuj kliknąć na przycisk dodaj lub zamknij. Nic się nie dzieje. Musimy oczywiście dodać odpowiednie akcje w kodzie.<br />
Zacznijmy od prostszej akcji zamykania okna. Najpierw dodajmy metodę '''act''' w klasie '''OknoZadania'''. W metodzie dodajmy sprawdzanie czy kliknięto przycisk zamknij. Jeśli tak ukryjmy okno metodą '''toogleShow'''. Na samej górze metody '''act''' wywołaj '''super.act()'''. Wywołanie '''super.act()''' spowoduje, że wywołana zostanie najpierw metoda act z dziedziczonej klasy '''Window'''. Dodano już tam odpowiedni kod i jeśli nie chcemy zmieniać podstawowego zachowania tej klasy, musimy wywołać właśnie tę instrukcję, która po prostu uruchomi metodą z dziedziczonej klasy.<br />
<small>''Użycie super to jedyny sposób do odwołania się do nadpisanej metody.''</small>
[[File:jmk-zadania_image117.png|center]]
Uruchom aplikację i zobacz czy pojawia się okno. Przetestuj działanie przycisku zamknij.
{| class="wikitable" border="1"
|Zadanie do wykonania:
* Pola po zamknięciu nie są czyszczone. Za pomocą metody '''setText''' ustaw wartości pól na puste po zamknięciu
|}
Teraz dodajmy akcję dla przycisku dodaj. Musi ona sprawdzać kliknięcie przycisku. Następnie musi pobrać dane z pola opis i termin wykonania. Następnie trzeba stworzyć zadanie i przypisać je do pola w klasie.<br />
Najpierw dodajmy pole w klasie '''OknoZadania''', które przechowa dodawane zadanie.
[[File:jmk-zadania_image138.png|center]]
Będziemy potrzebować klasy '''java.util.Date'''. Dodajmy odpowiedni import w klasie '''OknoZadania'''.
[[File:jmk-zadania_image98.png|center]]
Teraz w metodzie '''act''' dodajmy kod, który sprawdzi wciśnięcie przycisku dodaj. pobierze dane z pól tekstowych. Stworzy nowe zadanie i przypisze do pola '''wprowadzoneZadanie'''. Na końcu musimy ukryć okno.
[[File:jmk-zadania_image141.png|center]]
Na razie nie pobieramy daty z pola terminu, zajmiemy się tym za chwilę. Póki co wywołujemy domyślny konstruktor '''new Date()''', który tworzy obiekt z aktualną datą.
{| class="wikitable" border="1"
|
Tutaj jako zadanie dodatkowe także możesz dodać czyszczenie pól.
|}
Teraz skompiluj program. Zobacz czy wszystko działa prawidłowo. Spróbuj wprowadzić dane i kliknij zapisz zadanie. Czy zostało dodane nowe?<br />
Oczywiście, że tak się nie stało. Brakuje jeszcze po stronie klasy '''ListaZadan''' sprawdzania czy stworzono w oknie nowe zadania. To w niej trzeba dodać zadanie na listę. Musimy móc je przekazać do tej klasy. Dodajmy jeszcze w klasie '''OknoZadania''' metodę '''pobierzWprowadzoneZadanie''', która zwróci wprowadzone zadanie, ale także je wyczyści.
[[File:jmk-zadania_image88.png|center]]
W ten sposób wywołując tę metodę możemy sprawdzić czy w oknie zostało dodane zadania i jeśli tak się stało pobrać je. Pole '''wprowadzoneZadanie''' musi być czyszczone, ponieważ jeśli za każdym wywołaniem tej metody zwracalibyśmy zadanie, to aplikacja dodawałaby je w nieskończoność i po prostu pojawiłby się błąd, a aplikacja zamknęłaby się.
{| class="wikitable" border="1"
|Jako ćwiczenie możesz sprawdzić co się stanie, gdy nie będzie czyszczenia wartość, czyli gdy usuniesz linię '''wprowadzoneZadanie = null'''.
|}
Przejdźmy do klasy '''ListaZadan''' i jej metody '''sluchajNaDodaniuZadania'''. Musimy umieścić w niej kod, który wywoła wcześniej dodaną metodę '''pobierzWprowadzoneZadanie''' z okna dodawania zadania. Jeśli zostanie zwrócona wartość, a nie null, to dodajmy zadanie na listę i uwaga, zmieniamy indeks aktualnie wyświetlanego zadania i samo aktualne zadanie na ostatnio dodane. Odświeżamy okno. Dzięki temu od razu będziemy mieli podgląd nowego zadania.
[[File:jmk-zadania_image96.png|center]]
Uruchom aplikację. Dodaj zadanie. Czy pojawiło się kolejne zadanie na liście?<br />
Teraz możemy wrócić do pobierania terminu wykonania. Aby pobrać termin musimy zmienić napis '''String''' na datę '''Date'''. W tym celu wykorzystamy znaną już nam klasę '''java.text.SimpleDateFormat'''.<br />
Przejdźmy do klasy '''OknoZadania'''. Dodajmy tam odpowiedni import.
[[File:jmk-zadania_image60.png|center]]
Teraz stwórzmy metodę, która pobierze nam z pola termin wykonania i zmieni go na datę. Utwórz metodę '''wczytajTermin''' w klasie '''OknoZadania'''. Jeśli pojawi się błąd w formacie zwróćmy aktualną datę.
[[File:jmk-zadania_image122.png|center]]
Najpierw tworzony jest obiekt '''parser'''. Pozwoli on nam na łatwą zamianę tekstu na datę. Następnie wywoływana jest metoda ''parser.<i style="color: green">parse</i>(<i style="color: orange">data</i>)'', która próbuje konwertować napis na obiekt klasy '''Date'''. Jeśli się nie uda pojawi się wyjątek który łapiemy przez zastosowanie try - catch, dzięki czemu wiemy, gdy data jest zła i będziemy mogli odpowiednio zareagować. Jest to sposób na walidację (sprawdzenie) daty.<br />
<small>'' Wyjątki są skomplikowanym mechanizmem. Mogą pojawić się, gdy użytkownik aplikacji zachowuje się w nieprzewidziany sposób, na przykład przekazując datę w nieznanym formacie lub gdy program został źle napisany. Jeśli nie przechwycimy niektórych wyjątków, aplikacja może się nie skompilować. Za pomocą '''try{ kod mogący powodować błąd }catch(Exception e){kod do wykonania w przypadku błędu}''', mówimy że jeśli w klamrach po '''try''' pojawi się błąd to należy go złapać ('''catch(Exception e)''') i wykonać kod po bloku '''catch. Exception e''' deklaruje nam wyjątek i pozwala użyć go za pomocą zmiennej e (można ją nazwać dowolnie), aby np. sprawdzić co poszło nie tak.''</small><br />
Teraz użyjmy tej metody przy tworzeniu zadania w metodzie '''act'''. Zmieńmy sposób tworzenia zadania, tak aby jako datę otrzymywał wprowadzony termin. Pamiętaj o przekazaniu do metody '''wczytajTermin''' wartości pola tekstowego '''terminWykonaniaBox'''.
[[File:jmk-zadania_image134.png|center]]
Teraz uruchom aplikację i dodaj nowe zadania. Wprowadź datę, pamiętaj, że musi być w formacie rok-miesiąc-dzień np. 1939-09-01. Dodaj też zadanie z błędną datą, powinno wtedy wprowadzić aktualną datę.<br />
To już wszystko. Jako ćwiczenia dodatkowe możesz wykonać rozszerzoną wersję zadania, gdzie będziesz miał możliwość zapisu danych do prawdziwej bazy danych.

== Lista zadań do wykonania - ZAAWANSOWANE ==

W tej części ćwiczenia prześlemy listę zadań przez internet do bazy danych '''Mongo''', którą założymy na stronie internetowej [[https://mongolab.com| https://mongolab.com]].<br />
Zaczynajmy! Przypominam, że potrzebna jest nasza wersja Greenfoot.

=== Rozszerzenie klasy zadania ===
Zacznijmy od stworzenia klasy '''IdObiektu'''. Będzie ona przechowywać jedno pole z id zadania. Dzięki id będziemy rozróżniali zadania. Będzie nam to potrzebne przy ich zapisie do bazy danych i późniejszym odczycie.<br />
'''Id''' traktuj jako unikalny identyfikator, coś co wyróżnia dany obiekt wśród innych. Możesz porównać ten identyfikator do numeru ucznia w dzienniku szkolnym. Każdy uczeń w klasie ma swój numer, który jest niezmienny. Podobnie zachowuje się identyfikator.<br />
Stwórz nową klasę: '''Edit -> New Class'''. W edytorze usuń pole x oraz przykładową metodę.<br />
Dodaj pole '''id''' typu '''String''', oraz stwórz dwie metody: '''getId''' oraz '''setId'''. Pierwsza z nich zwraca '''id''', a druga ustawia '''id'''.
[[File:jmk-zadania_image33.png|center]]
Metody muszą nazwać się '''get(NazwaPola)''' i '''set(NazwaPola)'''. Jest to pewien standard, który w tym wypadku będzie nam potrzebny do przesyłania danych do bazy danych.<br />
Dodajmy od razu jeszcze jedną rzecz. Jest to pewna ciekawostka.<br />
Najpierw dodaj, na samej górze otwartej w edytorze klasy, linię z potrzebnym dla nas importem.
[[File:jmk-zadania_image32.png|center]]
Następnie dodaj taką konstrukcję przy polu '''id'''. Bardzo ważna jest tutaj kolejność.
[[File:jmk-zadania_image13.png|center]]
'''@SerializedName''' jest adnotacją. Dostarcza ona dodatkowych informacji, które są potrzebne przy przesyłaniu danych do bazy '''Mongo'''. We fragmencie kodu, który właśnie napisaliśmy, mówimy kompilatorowi, żeby traktował to pole jakby miało nazwę '''$oid'''. Musimy zastosować adnotacją nad nazwą pola, jest to wymóg Javy, aby adnotacja pojawiała się nad: polem, metodą, klasą.<br />
Skompiluj aplikację. Zobacz czy wszystko działa poprawnie i nie zrobiliśmy nigdzie błędu.<br />
Przejdźmy do klasy '''Zadanie'''. Przechowuje ona dane o zadaniu. Dodajmy nowy import.
[[File:jmk-zadania_image110.png|center]]
Następnie rozszerzmy klasę zadania, dodając pole '''id''' o typie '''IdObiektu''', z odpowiednią adnotacją.
[[File:jmk-zadania_image19.png|center]]
Podobnie jak w klasie '''IdObiektu''', dodajemy tutaj adnotację do pola '''id''', podając własną nazwę.<br />
Potrzebujemy jeszcze metod pobierających i ustawiających id ('''get''' oraz '''set''').
[[File:jmk-zadania_image18.png|center]]
Takie rozszerzenie pozwoli przechowywać nam dane w bazie '''Mongo'''.

=== MongoDB ===
Mongo jest bazą danych. Oznacza to, że można w niej przechowywać interesujące nas informacje, które mają być bezpiecznie i trwale zachowane.<br />
Jeśli pamiętasz przykład z zapisem ocen do pliku, to być może pamiętasz, że zapis pozwalał na ponowny odczyt danych z pliku. Załadowanie poprzedniego stanu aplikacji, tzn. ocen i uwag, uczniów i przedmiotów, które były wprowadzone wcześniej.<br />
Baza danych pozwala na coś podobnego, jednak ma kilka innych zalet. Może być udostępniana wielu aplikacjom jednocześnie, nawet jeśli są uruchomione na różnych komputerach. Baza danych ma też wiele mechanizmów, które dają dodatkowe bezpieczeństwo.<br />
W ćwiczeniu z listą zadań masz okazję użyć bazy danych. Jest to wcześniej wspomniana baza danych '''Mongo'''. Wybraliśmy ją także dlatego, że w internecie można założyć bezpłatnie bazę danych i z niej korzystać. Wystarczy mieć dostęp do sieci.

==== Założenie bazy danych ====
Bezpłatnie bazę danych możemy założyć przez serwis [[https://mongolab.com/| https://mongolab.com/]].<br />
Wejdź na powyższą stronę.
[[File:jmk-zadania_image86.png|center]]
W prawym górnym rogu wybierz '''Sign up''' (zarejestruj). Wprowadź swoje dane logowania. Pamiętaj o kliknięciu w link '''Master Services Agreement''' i zaznaczeniu ptaszka przy '''I accept MongoLab’s''' … .
'''Account name''' może być takie jak '''Username'''.
[[File:jmk-zadania_image12.png|center]]
Zostaniesz przekierowany na nową stronę. Pojawi się informacja o konieczności potwierdzenia adresu email.
[[File:jmk-zadania_image108.png|center]]
Wejdź na swoje konto mailowe i otwórz email z prośbą o weryfikację ('''Verify your MongoLab email address''').
[[File:jmk-zadania_image53.png|center]]
[[File:jmk-zadania_image135.png|center]]
Kliknij na wskazany link. Zostaniesz przekierowany na swoje konto w MongoLab.
[[File:jmk-zadania_image23.png|center]]
Teraz stwórzmy nową bazę danych, której użyjemy w niniejszym projekcie . Masz do wykorzystania 512 MB, co jest całkiem sporą ilością danych (o ile nie będziesz tam przechowywać plików: filmów, muzyki, obrazów, dokumentów etc.).<br />
Przejdź na stronę główną.
[[File:jmk-zadania_image26.png|center]]
Wybierz '''Create new'''. Pojawi się nowa strona z ustawieniami Twojej bazy.
[[File:jmk-zadania_image123.png|center]]
Wybierz na samej górze ikonkę jedną z ikon. Są to nazwy dostawców, którzy rzeczywiście będą przechowywać dane. W tej chwili nie ma znaczenia co wybierzesz, bo wszyscy oferują darmową wersję do wspomnianego 0,5 GB danych.<br />
Poniżej wybierz '''Single-node'''. Na dole w sekcji '''Standard Line''' wybierz '''Sandbox'''.
[[File:jmk-zadania_image73.png|center]]
Przejdź poniżej i wybierz MongoDB version '''2.6.x.'''<br />
Jeśli widzisz sekcję '''Security''' wybierz '''Not applicable'''. Jest ona widoczna tylko dla niektórych dostawców.<br />
Podaj nazwę swoje bazy danych np. '''zadania'''.<br />
Wybierz '''Create new MongoDB deployment'''.
[[File:jmk-zadania_image07.png|center]]
Zostanie stworzona baza danych i następuje przekierowanie na ekran do jej zarządzania.
[[File:jmk-zadania_image94.png|center]]
Kiedy klikniesz na nazwę bazy danych, nastąpi przekierowanie na ekran, w którym możesz nią zarządzać.
[[File:jmk-zadania_image10.png|center]]
Widzisz ostrzeżenie, że nie posiadasz użytkownika i należy stworzyć nowego. Kliknij na zakładkę '''Users'''.
[[File:jmk-zadania_image67.png|center]]
Wybierz '''Add database user''' i wprowadź dane użytkownika bazy danych.
[[File:jmk-zadania_image89.png|center]]
Kliknij '''Create'''-na liście użytkowników zobaczysz właśnie stworzonego.
[[File:jmk-zadania_image128.png|center]]
Żeby korzystać ze stworzonej przez nas aplikacji, musisz zrobić jeszcze jedną rzecz, a mianowice pozwolić na dostęp do aplikacji przez internet (dokładniej mówiąc - przez serwisy '''REST''' wystawione na protokole '''HTTP''').<br />
W tym celu kliknij na nazwę użytkownika w prawym górnym rogu.
[[File:jmk-zadania_image37.png|center]]
Pojawi się ekran do zarządzania użytkownikiem.<br />
Możesz tutaj zmienić nazwę użytkownika, adres email, hasło, a nawet wprowadzić dwustopniową autoryzację.<br />
Nas jednak interesuje sekcja umieszczona na samym dole.
[[File:jmk-zadania_image146.png|center]]
Podany tam jest '''API Key'''. Jest to klucz, który pozwala nam łaczyć się z naszym kontem w MongoLab i korzystać z naszych baz danych.<br />
Poniżej jest jeszcze możliwość włączenia dostępu przez serwisy korzystające z '''REST'''. Włączenie tej opcji nie jest niezbędne do korzystania przez aplikację do zarządzania zadaniami.<br />
Wybierz '''Enable Data API access'''.
[[File:jmk-zadania_image95.png|center]]
Potwierdź klikając '''Enable'''.<br />
Status powinien zmienić się na '''Enabled''' - tak, jak poniżej.
[[File:jmk-zadania_image40.png|center]]
Na razie to wszystko. Możesz już korzystać z '''MongoDB''' w aplikacji do zarządzania zadaniami.

==== JSON ====
JSON jest sposobem przechowywania i przesyłania danych. W informatyce wykorzystuje się bardzo wiele sposobów przechowywania danych.<br />
Zawsze jednak można traktować te zapisy jako dane binarne lub tekstowe.<br />
Różnica jest taka, że dane tekstowe są czytelne dla użytkownika. Przykładem jest zwykły plik tekstowy, na przykład taki, jak poniżej:.
[[File:jmk-zadania_image24.png|center]]
Jednak, gdy w notatniku otworzysz plik '''pdf''', to zobaczysz dane podobne do tych poniżej. Ewidentnie nie da się ich odczytać bez rozumienia ich “zapisu binarnego”.
[[File:jmk-zadania_image68.png|center]]
Dane w formacie '''JSON''' są danymi tekstowymi. Są czytelne dla człowieka. Ich zapis także je strukturyzuje. Jeśli przyjrzysz się jak wyglądają dane zapisane w '''MongoDB''' i przyjrzysz się klasie '''Zadania''' z aplikacji, to możesz zauważyć, że zapis odzwierciedla jednoznacznie pola w klasie.
[[File:jmk-zadania_image125.png|center]]
[[File:jmk-zadania_image03.png|center]]
Wszystkie pola są odzwierciedlone w zapisie '''JSON'''. Obiekty zagnieżdżone są także objęte klamrą {}.<br />
W zapisie JSON po lewej stronie dwukropka ( : ) znajdują się klucze (w Javie odpowiadają nazwom pól), a po prawej wartości.<br />
Przecinek pozwala na wymienienie kolejnych pól.<br />
Klamry '''{}''' mówią, że jest opisywany obiekt (w Javie nazywamy go klasą). Obiekty mogą być zagnieżdżone.<br />
Z kolei, gdy chcesz wymienić kilka obiektów po sobie, stosujesz nawiasy kwadratowe '''[ ]'''.
[[File:jmk-zadania_image82.png|center]]

==== Klasa SerwisMongo ====
Jest to klasa, która pozwala na zarządzanie bazą w '''MongoDB'''. Przyjrzyjmy się jej dokładniej.
[[File:jmk-zadania_image85.png|center]]
W klasie na początku zdefiniowane jest pole ze słowami '''static final'''. Oznacza to stałą: wartość, która jest zawsze taka sama i nie można jej zmienić. Tutaj jest podany adres bazowy dostępu do bazy danych.<br />
Kolejne trzy pola to '''API Key'''. Jak wspomniałem w poprzednim rozdziale jest to wartość, po której możesz dostać się do bazy danych.<br />
Następne pola to '''nazwaBazy''' i '''nazwaKolekcji'''. Są to dwa pola, które przechowują informację o tym, w jakiej bazie i w jakiej kolekcji przechowywać zadania. Możesz tworzyć różne bazy danych dla różnych aplikacji. Możesz mieć również różne kolekcje obiektów (np. zadań), dla różnych użytkowników. Tak aby mieć łatwy dostęp do danych konkretnego użytkownika.<br />
Pole '''klasa''' mówi, jakie obiekty będzie przechowywać i odczytywać za pomocą tego serwisu.<br />
Wszystkie te wartości podajesz w konstruktorze i są one wymagane, aby połączyć się z bazą danych i pracować z serwisem.
[[File:jmk-zadania_image76.png|center]]
Jeśli spojrzałeś w kod klasy, to pewnie zauważyłeś jest jeszcze jedno pole.
[[File:jmk-zadania_image145.png|center]]
'''Gson''' jest to obiekt, który pozwala na zamianę obiektu klasy na zapis w postaci napisu ('''String'''), w formacie '''JSON'''. Dane w postaci '''JSON''' są wysyłane i odbierane z bazy '''MongoDB''', dlatego jest nam potrzebna taka klasa. Korzystamy z dwóch metod.
[[File:jmk-zadania_image111.png|center]]
Lub bardziej przejrzyście.
<syntaxhighlight lang="java">
Zadanie zapisaneZadanie = gson.fromJson(rezultat, Zadanie.class);
</syntaxhighlight>
Metoda '''fromJson''' konwertuje napis '''rezultat''' na obiekt klasy '''Zadanie'''.
[[File:jmk-zadania_image142.png|center]]
Metoda toJson przekształca obiekt klasy '''Zadanie''' na napis ('''String''').<br />
Jest jeszcze jedno wywołanie, trochę bardziej skomplikowane.
[[File:jmk-zadania_image93.png|center]]
To wywołanie konwertuje napis na tablicę specjalnych obiektów '''Json'''.<br />
W klasie '''ZadaniaMongo''' mamy też trzy metody. Postaram się opisać każdą z nich.<br />
Metoda '''dodajZadanie'''.
<syntaxhighlight lang="java">
public T dodajDoKolekcji(T obiekt) {
try {
String adresPobieraniaKolekcji = MONGO_URL + nazwaBazy + "/collections/" + nazwaKolekcji + "?apiKey=" + apiKey;
String obiektString = gson.toJson(obiekt);
String rezultat = Request.Post(adresPobieraniaKolekcji)
.bodyString(obiektString, ContentType.APPLICATION_JSON)
.execute()
.returnContent().asString();
T zapisaneZadanie = gson.fromJson(rezultat, klasa);
return zapisaneZadanie;
} catch (IOException e) {
System.out.println("Nieudany zapis obiektu");
e.printStackTrace();
}
return null;
}
</syntaxhighlight>
Metoda przyjmuje obiekt, który ma zapisać. Zwraca dodany obiekt. Nie widać tego w kodzie, ale ma uzupełnione id.<br />
W linijce 3 tworzony jest adres '''URL''' (taki adres jaki widzisz i wpisujesz w przeglądarce internetowej w pasku adresu), na przykład:
[[File:jmk-zadania_image103.png|center]]
Tutaj jest to adres, który trzeba podać, aby dodać obiekt.<br />
W linii 4 zapisana jest zmiana obiektu na napis. Tak, aby był czytelny dla '''MongoDB'''.
[[File:jmk-zadania_image36.png|center]]
Obiekt, który będziemy przesyłać do '''Mongo''', nie ma podanego id.<br />
W linijce 5 zaczyna się bardziej skomplikowana operacja. Na klasie '''Request''' wywoływana jest metoda '''Post''' z podanym adresem URL. W linijce 6, na zwróconym przez metodę '''Post''' obiekcie, wywołana jest metoda '''bodyString''' z przekazanymi danymi w postaci napisu (stworzone w 4 linii).<br />
W linii 6 wywoływane jest żądanie. Dopiero teraz przez internet wysyłana jest informacja do '''MongoDB''' z żądaniem dodania nowego obiektu.<br />
Linia 7 jest wywołana, gdy '''Mongo''' doda obiekt i zwróci odpowiedź. Ta linijka mówi, aby zwrócona odpowiedź została pobrana i zamieniona na napis ('''String'''). Napis jest przypisany do zmiennej '''rezultat'''. W tej zmiennej powinniśmy mieć taki oto napis (bez numerów linii):
[[File:jmk-zadania_image107.png|center]]
Teraz pozostaje nam już tylko przekonwertować napis na obiekt klasy, który został podany w konstruktorze serwisu. Odpowiada za to linijka 10.<br />
Zaś w linijce 11 obiekt jest zwracany jako wynik wywołania metody.<br />
W linijkach 2 oraz 12-15 mamy przechwycenie wyjątku. Jest to związane z tym, że podczas łączenia się z internetem dochodzi do różnych sytuacji (jak np. brak zasięgu), w których dane nie mogą zostać zwrócone. Dlatego niektóre metody klasy Request wyrzucają wyjatek IOException, który przechwytujemy i wypisujemy.<br />
Jeśli wystąpił wyjątek, to kod dojdzie do linijki 16 i zostanie zwrócony '''null'''.<br />
Pozostałe dwie metody to:
* '''pobierzWszystkie''' - zwraca listę obiektów z kolekcji
* '''aktualizuj''' - zmienia obiekt o podanym '''id''' jako pierwszy parametr.
Obie metody są bardzo podobne. Różnica polega głównie na wywołaniu adresów i rodzaju wywołań ('''Post, Get, Put''') oraz przesyłanych danych.
Co i jak trzeba przesyłać opisane jest w dokumentacji '''MongoDB'''. Jest ona dostępna pod adresem: [[http://docs.mongolab.com/data-api/| http://docs.mongolab.com/data-api/]].

=== Dodanie zapisu zadań w bazie Mongo ===
Teraz dodamy kod, który pozwoli zapisać nasze zadania w bazie danych.<br />
Zacznijmy od dodania pola z serwisem w klasie '''ListaZadan''', który będzie odpowiadał za obsługę '''MongoDB'''. Znajdź swój '''ApiKey''', nazwę bazy danych i kolekcji. Jeśli pracujesz z kilkoma uczniami lub kolegami, możesz korzystać z jednego '''ApiKey''' i jednej bazy danych -podajesz tylko inne nazwy kolekcji.
[[File:jmk-zadania_image35.png|center]]
Teraz na starcie aplikacji zaczytajmy listę zadań. Zrób to w konstruktorze, przed wywołaniem metody '''odswiezEkran'''.
[[File:jmk-zadania_image87.png|center]]
Usuń stary kod tworzenia listy zadań. Trzeba także sprawdzić czy pobrana lista nie jest pusta. Musimy też w metodzie odśwież zadanie sprawdzać czy '''aktualneZadanie''' nie jest '''nullem'''.
[[File:jmk-zadania_image70.png|center]]
W przeciwnym wypadku pojawi się błąd.<br />
W metodzie '''sluchajNaDodaniuZadania''' przed dodaniem do listy zapisz obiekt w bazie danych. Następnie zwrócone przez '''dodajDoKolekcji''' zadanie dodaj do listy zadań, wyświetlanej na ekranie.
[[File:jmk-zadania_image75.png|center]]
Ostatni krok to poprawienie metody '''inicjujPodgladZadania''', aby ustawiała na starcie puste pola.
[[File:jmk-zadania_image132.png|center]]
Uruchom aplikację i przetestuj dodawanie. Następnie zamknij aplikację i uruchom ponownie. Czy zadania się pojawiły?<br />
Sprawdź jeszcze czy nowo dodane zadania faktycznie pojawiły się w bazie '''MongoDB'''.

=== Pogląd danych ===

Gdy uruchomisz aplikację i dodasz kilka pierwszych zadań możesz na stronie '''MongoLab''' przejrzeć dane, które zapisałeś do bazy danych.<br />
W tym celu zaloguj się do konta '''MongoLab''' na stronie [[https://mongolab.com/| https://mongolab.com/]].<br />
Przejdź do swojej bazy danych klikając na jej nazwę.
[[File:jmk-zadania_image116.png|center]]
Pojawi się ekran z kolekcjami (traktuj je jak grupy danych).
[[File:jmk-zadania_image127.png|center]]
Po wejściu na kolekcje można zobaczyć, co jest w niej przechowywane. Wygląda to mniej więcej tak, jak na zrzucie ekranu poniżej.
[[File:jmk-zadania_image80.png|center]]
Można tutaj dokładnie odczytać jak wyglądają dane.<br />
Przyglądając się pierwszemu wpisowi można odnaleźć id obiektu, ale także opis, datę wykonani. Czyli jest to dokładnie to samo co można zobaczyć w aplikacji '''Zadań'''. Jedyna różnica jest taka, że id obiektu ukrywamy przed użytkownikiem.<br />
Uwaga! Te dane możesz zmienić lub je usunąć, wystarczy wybrać odpowiednią opcję po prawej stronie wiersza.

=== Zapis do pliku ===
W tej części dodamy zapis danych do pliku tekstowego.<br />
Zacznijmy od dodana przycisku jako pole w klasie '''ListaZadan'''.
[[File:jmk-zadania_image00.png|center]]
Teraz ustawmy ten przycisk na ekranie. Dodajmy metodę '''inicujPrzyciskiPlikow''', w której stworzymy przycisk i dodamy na ekran.
[[File:jmk-zadania_image52.png|center]]
Wywołajmy metodę w konstruktorze '''ListaZadan'''.
[[File:jmk-zadania_image130.png|center]]
Uruchom aplikację i zobacz czy przycisk się pojawia.
[[File:jmk-zadania_image97.png|center]]
Teraz dodajmy metodę, która będzie zapisywać dane. Nazwijmy ją '''zapiszDane'''. Musi ona przejść po liście zadań. Stworzyć dla każdego zadania napis, rozdzielając wartości średnikiem ;. Wszystkie wartości ma dodać na listę, a listę zapisać za pomocą klasy '''Pliki''' i metody '''zapisz'''.
[[File:jmk-zadania_image124.png|center]]
Najpierw tworzymy formater daty. Następnie w pętli '''for''' idziemy zadanie po zadaniu. Dla każdego zadania zamieniamy datę na napis. Później tworzymy linię, zwróć uwagę, że z pola id zadania jest pobierane kolejne id, gdyż to jest wartość napisu. Wartości w linii rozdzielamy średnikiem, aby móc łatwo rozdzielić wartości przy wczytaniu danych.<br />
Linię dodajemy do listy. Na końcu wywołujemy zapis do pliku o nazwie “'''plik.csv'''”.<br />
Teraz musimy dodać metodę nasłuchującą na wciśnięcie przycisku. Nazwij ją '''sluchajNaPrzyciskachPlikow'''. I wywołać w niej '''zapiszDane'''.
[[File:jmk-zadania_image45.png|center]]
Teraz metodę słuchającą wywołaj w metodzie '''act'''.
[[File:jmk-zadania_image49.png|center]]
Uruchom aplikację, upewnij się, że istnieją zadania, jeśli nie to dodaj nowe i wciśnij przycisk '''zapisz plik'''.
Teraz przejdź do katalogu w którym jest projekt. Powinien się naleźć w nim plik '''plik.csv'''. Otwórz go za pomocą notatnika lub w programie '''excel''' lub '''calc'''. Powinieneś zobaczyć dane podobne do tych.
[[File:jmk-zadania_image46.png|center]]
Lub w '''excel'''.
[[File:jmk-zadania_image04.png|center]]
Są tu kolejno zapisane: '''id zadania, opis, data'''.

=== Odczyt pliku ===
Teraz dodamy jeszcze funkcjonalność odczytu z pliku.<br />
Zacznijmy podobnie od zdefiniowania pola przycisku wczytajPlik.
[[File:jmk-zadania_image126.png|center]]
Następnie dodaj go na ekranie, dodając inicjację w metodzie '''inicjujPrzyciskiPlikow'''.
[[File:jmk-zadania_image56.png|center]]
Uruchom aplikację i sprawdź czy pojawił się nowy przycisk.
[[File:jmk-zadania_image64.png|center]]
Teraz musimy dodać metodę, która pobierze dane. Nazwij ja '''wczytajDane'''.
[[File:jmk-zadania_image01.png|center]]
W metodzie tej najpierw za pomocą klasy '''Pliki''' i metody '''wczytaj''' pobierany jest wcześniej zapisany plik. Dane są pobrane za pomocą listy linii, podobnie jak zapisywaliśmy.<br />
Następnie tworzony jest '''formaterDaty''' potrzebny do zamiany daty na napis. Następnie czyścimy aktualną listę zadań.<br />
W kolejnym kroku idziemy w pętli '''for''' linia po linii. Każdą linię rozdzielamy po średniku metodą '''split''', która tworzy tablice. W pierwszej wartości tablicy będzie '''id''', w kolejnej '''opis''', a w ostatniej '''terminWaznosci'''.<br />
Najpierw odczytujemy datę za pomocą formatera. Kod jest podobny do tego, który został użyty przy odczycie wartości z pola '''terminWaznosci''' w oknie dodawania zadania.<br />
Później tworzone jest zadania. Następnie obiekt '''IdObiektu''', do którego przypisujemy '''id'''. Zadanie dodajemy do list.<br />
Na końcu zerujemy indeks aktualnegoZadania, jeśli lista nie jest pusta ustawiamy nowe aktualne zadanie i odświeżamy ekran.<br />
Teraz musimy tę metodę wywołać po wciśnięciu przycisku '''wczytajPlik'''. Do metody '''sluchajNaPrzyciskachPlikow''' dodaj odpowiedni kod.
[[File:jmk-zadania_image102.png|center]]
Uruchom aplikację. Dodaj zadanie i zapisz plik. Następnie dodaj kolejne zadanie. Później kliknij '''wczytaj plik'''. Zobacz jakie są zadania. Powinno brakować zadań, które dodałeś po zapisie pliku.
Biurokrata, administrator
530
edycji