Celem laboratorium jest zapoznanie się z mechanizmem wyjątków oraz poprawienie wydajności rysowania zwierząt na mapie.
Treść laboratorium powstała we współpracy z Norbertem Morawskim.
Najważniejsze zadania:
- Dodanie obsługi wyjątków.
- Dodanie klasy
MapBoundary
. - Szkielet aplikacji JavaFX.
- Wyświetlanie mapy w GUI.
- Wyjątki są mechanizmem pozwalającym przekazywać informację o błędzie pomiędzy odległymi fragmentami kodu.
- Zgłoszenie błędu odbywa się poprzez rzucenie wyjątku. W Javie służy do tego słowo kluczowe
throw
:
throw new IllegalArgumentException("ABC argument is invalid")
- Nieobsłużony wyjątek powoduje przerwanie działania aplikacji.
- Obsługa wyjątków odbywa się za pomocą mechanizmu przechwytywania wyjątków. W Javie służy do tego konstrukcja
try...catch
:
try {
// kod który może rzucić wyjątek
} catch(IllegalArgumentException ex) {
// kod obsługi wyjątku
}
Wyjątek może być rzucony na dowolnym poziomie w kodzie, który otoczony jest blokiem try
. Tzn. w kodzie tym może być
wiele zagnieżdżonych wywołań funkcji, a i tak blok try
przechwyci taki wyjątek, pod warunkiem, że nie zostanie on obsłużony
na niższym poziomie.
- JavaFX to framework do obsługi środowiska graficznego.
- Aplikacja JavaFX składa się z:
Stage
- okno aplikacji,Scene
- aktualna zawartość aplikacji (np. ekran symulacji, ekran podsumowania)- Scena zawiera wiele instancji
Node
. Są nimi m.in. przyciski, pola tekstowe, kontenery (VBox
,HBox
,GridPane
, itp.).
- Główna klasa w aplikacji powinna dziedziczyć po
Application
i implementować metodęstart()
. - Minimalna aplikacja powinna stworzyć jedną scenę, przypiąć ją do
Stage
i wyświetlić.
- Wykorzystaj klasy z laboratorium 6.
- W metodzie odpowiedzialnej za zamianę argumentów aplikacji na ruchy zwierzęcia rzuć wyjątek
IllegalArgumentException
, jeśli którykolwiek z parametrów nie należy do listy poprawnych parametrów (f
,forward
,b
,backward
, etc.). Jako przyczynę wyjątku wprowadź łańcuch znaków informujący, że określony parametr jest niepoprawny, np.new IllegalArgumentException(argument + " is not legal move specification")
. - W metodach odpowiedzialnych za dodawanie elementów do mapy, jeśli dodanie elementu na wybrane pole jest niemożliwe
rzuć wyjątek
IllegalArgumentException
, podając jako przyczynę łańcuch znaków zawierający informację o tym, które pole jest błędne. Wyjątek zastępuje sygnalizowanie błędu przy pomocy zwracania wartościfalse
. - Obsłuż oba wyjątki w metodzie
main
klasyWorld
. Obsługa powinna polegać na wyświetleniu komunikatu wyjątku oraz zakończeniu działania programu, a konstrukcjatry
powinna obejmować cały kod znajdujący się w metodziemain
. - Przetestuj działanie wyjątków poprzez podanie nieprawidłowego parametru ruchu oraz dodanie do mapy dwa razy tego samego zwierzęcia. Efektem powinno być kontrolowane zakończenie działania programu.
- Zaktualizuj testy metody
place
oraz klasyOptionsParser
, aby były zgodne z nowym kontraktem.
- Dodaj nową klasę
MapBoundary
, która będzie odpowiedzialna za przechowywanie informacji o obszarze zajmowanym przez obiekty na mapie. - Klasa ta powinna implementować interfejs
IPositionChangeObserver
. - Klasa
MapBoundary
powinna zawierać dwa zbiory uporządkowane obiektów na mapie - jeden wzdłuż osi X, drugi wzdłuż osi Y.Ponieważ porządek musi być zupełny, w przypadku obiektów o tym samym indeksie wzdłuż danej osi porównaj drugą współrzędną oraz typ obiektu (zwierzę/trawa).Przemyśl jakie dokładnie obiekty należy trzymać w tych zbiorach. Czy mogą to być obiektyAnimal
? Co się stanie gdy zwierzę zmieni pozycję? - Dodanie obiektu do mapy
GrassField
(UnboundedMap
) powinno skutkować dodaniem tego obiektu do instancjiMapBoundary
. - Obiekty mają być dodawane w ten sposób, że skrajne pozycje na liście zawsze zajmowane są przez obiekty które mają odpowiednio największą oraz najmniejszą wartość indeksu w danym wymiarze.
- Mapa powinna korzystać z instancji klasy
MapBoundary
w celu efektywnego obliczania obszaru, który ma zostać wyświetlony.
- W
build.gradle
:- Dodaj
id 'org.openjfx.javafxplugin' version '0.0.13'
do sekcjiplugins
. - Dodaj pod
repositories
sekcję:javafx { version = "17" modules = [ 'javafx.controls' ] }
- Dodaj
- Utwórz nowy pakiet
agh.ics.oop.gui
. - Odśwież konfigurację Gradle'a (
Ctrl+Shift+O
). - Utwórz klasę
App
dziedziczącą zApplication
z pakietujavafx.application
. - Zaimplementuj metodę
public void start(Stage primaryStage)
.- Będzie to metoda uruchamiająca interfejs graficzny Twojej aplikacji.
- Na razie możesz w ciele metody wpisać
primaryStage.show();
. Wyświelti to puste okno aplikacji.
- W metodzie
main
wWorld
dodajApplication.launch(App.class, args);
- Spowoduje to uruchomienie okna JavaFX.
- Zobacz czy okno pokazuje się (może być nieresponsywne, ale powinno się pokazać).
Pamiętaj, żeby importować brakujące klasy z pakietu
javafx
- W klasie
App
dodaj:Spowoduje to:Label label = new Label("Zwierzak"); Scene scene = new Scene(label, 400, 400); primaryStage.setScene(scene); primaryStage.show();
- Utworzenie nowej etykiety z treścią
Zwierzak
. - Utworzenie sceny zawierającej tylko obiekt
Label
, z wymiarami 400 x 400 pikseli. - Ustawienie sceny jako aktywnej.
- Pokazanie okna aplikacji.
- Utworzenie nowej etykiety z treścią
- Zmodyfikuj kod tak, aby zamiast pojedynczej etykiety wyświetlał siatkę z etykietami.
- Wskazówka: Skorzystaj z klasy
GridPane
. Przydatne może być ustawieniegrid.setGridLinesVisible(true);
.
- Wskazówka: Skorzystaj z klasy
- Przenieś kod inicjalizacyjny mapy z klasy
World
doApp
(możesz skorzystać z metodyinit()
). - Użyj
getParameters().getRaw()
żeby odczytać parametry linii komend. - Wykorzystaj
AbstractWorldMap
, tak aby siatka odpowiadała rozmiarom i orientacji mapy. Pamiętaj, że współrzędney
rosną w kierunku "do góry", wGridPane
jest na odwrót. - Dodaj w pierwszej kolumnie i w pierwszym rzędzie wartości współrzędnych (podobnie jak robi to
MapVisualizer
). - Do pozostałych wierszy dodaj obiekty z mapy.
- Dodaj do siatki rozmiary kolumn i wierszy. Skorzystaj z:
grid.getColumnConstraints().add(new ColumnConstraints(width));
grid.getRowConstraints().add(new RowConstraints(height));
- Wyśrodkuj etykiety korzystając z wywołania
GridPane.setHalignment(label, HPos.CENTER)
. - Aktualnie, twój program powinien wyglądać mniej więcej tak (użyto mapy
GrassField
, dodano 2 zwierzaki):
- Otaguj gotowe rozwiązanie jako lab7.