Przyszedł czas na utworzenie bardziej złożonej aplikacji w JavieFX, przy czym będziemy to robić z wykorzystaniem FXML i SceneBuildera. Jeśli tego nie robiłeś warto najpierw zapoznać się z lekcją w której wyjaśniłem jak się tworzy taki projekt, jak można zainstalować SceneBuildera i jak z niego korzystać we współpracy z NetBeans.
W aplikacji, którą zbudujemy użyjemy nowych kontrolek i kontenera, pokażę także jak otwierać pliki oraz utworzymy wykres.
Tworzenie projektu i podstawowej formy interfejsu
Zacznijmy, oczywiście od stworzenia nowego projektu („JavaFX FXML Application”) o nazwie „DaneFX”. Pokaże się domyślny projekt z przyciskiem (Button
) i etykietą (Label
). Etykieta nie jest widoczna, ale można ją zobaczyć klikając po lewej stronie w rozwijanym okienku Hierarchy
na element Label
.
Zaczniemy od usunięcia zawartości aplikacji. Kliknij w okienku Hierarchy
na AnchorPane
i usuń go wybierając Menu->Edit->Delete
lub używając odpowiedniego skrótu klawiszowego (można go podejrzeć w Menu). SceneBuilder najpierw upewni się, że wiemy co robimy:
Zatwierdzamy klikając na Delete
. Centralna część SceneBuildera staje się pusta i szara z zaznaczonym prostokątem zachęcającym nas „Drag Library items here…”
Teraz wędrujemy do palety kontrolek po lewej stronie i tam w sekcji Containers
, znajdujemy BorderPane
.
Przeciągamy kontener do szarego obszaru, teraz widzimy znów zarys okna:
Niestety teraz nie widać struktury kontenera BorderPane
, choć w okienku Hierarchy
można dowiedzieć się, że składa się on z pięciu części: TOP
, BOTTOM
, LEFT
, RIGHT
oraz CENTER
. Ich układ uwidoczni się, kiedy przeciągniemy na niego pierwszy element. Będzie to pasek menu. Rozwiń w palecie kontrolek sekcję Controls
(nie Menu
!) i tam zlokalizuj MenuBar
. Przeciągnij go na obszar projektowanej aplikacji, wtedy pojawi się struktura kontenera BorderPane
.
Jak widać nazwy obszarów odpowiadają nazwom, co więcej jest to układ, który sprawdza się dla wielu aplikacji. W części górnej (TOP
) zwykle umieszcza się menu, na dole (BOTTOM
) można umieścić np. pasek wyświetlający różne informacje a w obszarach pomiędzy nimi umieszcza się inne elementy interfejsu. Oczywiście nie musimy wykorzystać wszystkich obszarów.
Pasek menu umieszczamy oczywiście na górze:
Rozwijając „drzewko” w okienku Hierarchy
widać strukturę utworzonego paska menu. Domyślnie posiada on trzy sekcje: File
, Edit
oraz Help
, a każdy z nich po jednym elemencie. Ponieważ nasza aplikacja nie będzie szczególnie skomplikowana, usuń od razu sekcje Edit
i Help
. Najłatwiej je zaznaczać w okienku Hierarchy
i usuwać.
Pozostała nam sekcja File
z elementem Close
. Zaznacz w Hierarchy
sekcję Menu
i po prawej w Inspektorze
zmień zawartość pola Text
z „File” na „Plik”.
Podobnie zrób z elementem menu, zmień „Close” na „Zamknij”.
Jeśli teraz uruchomisz aplikację z poziomu NetBeans (nie zapomnij wcześniej zapisać pracy w SceneBuilderze) to zobaczysz, ze aplikacja posiada rozwijane menu, ale do elementu Zamknij
nie jest przypisana żadna akcja. Trzeba więc to zmienić.
Modyfikujemy menu
Najpierw zaznacz w okienku z hierarchią element odpowiadający elementowi menu. Następnie w inspektorze wybierz sekcję Code
i tam wypełnij pole fx:id
wartością „menu_zamknij” a następnie w polu OnAction
umieść nazwę metody: „zamknijAplikacjeAction”.
Niestety, SceneBuilder (na razie) w takiej sytuacji nie utworzy automatycznie odpowiedniej metody, co więcej kiedy usunęliśmy kontener AnchorPane
utraciliśmy powiązanie elementów opisanych w pliku FXMLDocument.fxml
z plikiem FXMLDocumentController.java
. Trzeba to poprawić.
Zacznijmy od modyfikacji pliku FXMLDocument.fxml
Znajdź tam znacznik otwierający dla konteneru BorderPane
, powinien wyglądać tak:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
Nie ma tu informacji o kontrolerze, w którym znajduje się wykonywalny kod, dodajmy go: fx:controller="danefx.FXMLDocumentController
. Część danefx
odpowiada nazwie pakietu, jeśli inaczej nazwałeś projekt/pakiet to zmodyfikuj odpowiednio ten element.
Teraz znacznik wygląda tak:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="danefx.FXMLDocumentController">
Czas zmodyfikować plik FXMLDocumentController.java
. Przede wszystkim usuwamy niepotrzebną już deklarację etykietki oraz kod przeznaczony dla usuniętego przycisku:
1 2 3 4 5 6 7 8 |
@FXML private Label label; @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); label.setText("Hello World!"); } |
Zamiast niego umieszczamy kod metody, którą przypisaliśmy dla elementu menu:
1 2 3 4 5 |
@FXML private void zamknijAplikacjeAction(ActionEvent event) { Platform.exit(); } |
Zadbaj jeszcze o zaimportowanie odpowiedniej biblioteki.
Teraz jeśli uruchomimy aplikację i wybierzemy z menu Zamknij
to program grzecznie się zamknie.
Dodajemy tabelę
Następną kontrolką jaką dodamy do aplikacji będzie TableView
. Jak sama nazwa wskazuje, ma ona formę tabeli, w której można umieszczać na przykład dane, co też zrobimy. Znajdź w palecie kontrolek w sekcji Controls
kontrolkę TableView
.
Przeciągnij ją do części LEFT
kontenera.
Po lewej stronie pojawiła się pusta tabela zawierająca domyślnie dwie kolumny. W razie potrzeby można dodać kolejne kolumny przeciągając je z palety na wstawioną tabelę ale w naszej aplikacji wystarczą dwie.
Kliknij na pierwszą kolumnę i w inspektorze w sekcji Properties
ustaw wartość pola Text
na „Seria”, będzie to nazwa kolumny widoczna w jej nagłówku. Analogicznie nadaj nazwę „Średnia” drugiej kolumnie. Wszystko zapisz.
Następnie znów kliknij na pierwszą kolumnę i w inspektorze przejdź do sekcji Code
. Tam ustaw wartość pola fx:id
na „nazwaColumn”, dla drugiej kolumny ustaw tą wartość na „sredniaColumn”. Po zapisaniu zmian plik FXMLDocument.fxml
powinien wyglądać tak:
1 2 3 4 5 6 7 |
<!--?xml version="1.0" encoding="UTF-8"?--> <!--?import java.lang.*?--> <!--?import java.util.*?--> <!--?import javafx.scene.*?--> <!--?import javafx.scene.control.*?--> <!--?import javafx.scene.layout.*?--> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Dodajemy dane do tabeli
Nasza tabela jest teraz przygotowana do przyjęcia danych. Dane nie będą skomplikowane, każdy rząd będzie odpowiadał jednej serii danych a właściwie nazwie tej serii i średniej z danych. Zaczniemy od stworzenia nowej klasy w naszym pakiecie o nazwie Seria.java
. Obiekt tej klasy, będzie przechowywał dane dla jednej serii. Klasa zawiera dwa pola „nazwa” i „średnia”, pierwsze z nich typu String
, drugie Double
. Ponadto posiada konstruktor przyjmujący jako argumenty wartości obu pól a także odpowiednie akcesory.
Kod klasy Seria.java
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package danefx; public class Seria { private String nazwa; private Double srednia; public Seria(String nazwa, Double srednia) { this.nazwa = nazwa; this.srednia = srednia; } public String getNazwa() { return nazwa; } public Double getSrednia() { return srednia; } } |
Docelowo dane będziemy wczytywać z pliku, ale tymczasem zrobimy to bezpośrednio z kodu. W metodzie initialize()
pliku FXMLDocumentController.java
umieść kod, a także nie zapomnij o deklaracjach tablicy i kolumn:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package danefx; import java.net.URL; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; public class FXMLDocumentController implements Initializable { // deklaracje obiektów @FXML private TableView tabela; @FXML private TableColumn<seria, string=""> nazwaColumn; @FXML private TableColumn<seria, double=""> sredniaColumn; @FXML private void zamknijAplikacjeAction(ActionEvent event) { Platform.exit(); } @Override public void initialize(URL url, ResourceBundle rb) { // Tworzenie listy i wypełnienie jej danymi ObservableList dane = FXCollections.observableArrayList( new Seria("seria 1", 2.3), new Seria("seria 2", 2.7), new Seria("seria 3", 3.0), new Seria("seria 4", 4.1), new Seria("seria 5", 2.0) ); // Ustawienie danych dla tabeli tabela.itemsProperty().setValue(dane); // Powiązanie pierwszej kolumny z polem nazwa obiektu typu Seria nazwaColumn.setCellValueFactory( new PropertyValueFactory<seria, string="">("nazwa") ); // Powiązanie drugiej kolumny z polem nazwa obiektu typu Seria sredniaColumn.setCellValueFactory( new PropertyValueFactory<seria, double="">("srednia") ); } } </seria,></seria,></seria,></seria,> |
W pierwszej części tworzymy kolekcję obiektów. JavaFX udostępnia własne wersje kolekcji i map implementujących i poszerzających interfejsy Set
, List
i Map
. Tutaj będziemy wykorzystywać observableArrayList
, pozostałe można znaleźć w dokumentacji pakietu javafx.collections
a o sposobach i przykładach ich użycia można poczytać np. w tutorialu firmy Oracle. Są one „obserwowalne” zatem można do nich np. dodawać listenery.
Jak widać, stworzyliśmy listę o nazwie dane
i wypełniliśmy ją obiektami typu Seria
, z których każdy posiada nazwę i wartość średniej. W kolejnych liniach kodu wiążemy listę z tablicą a poszczególne kolumny z polami obiektów.
Jeśli teraz uruchomimy aplikację, uzyskamy taki rezultat:
Jak widać, tabela jest trochę za szeroka. Można temu zaradzić, ustawiając dla tabeli w zakładce Layout
inspektora wartość pola Pref Width
na 150.
Projekt w NetBeans (spakowany w pliki .zip) na tym etapie można pobrać tutaj.
Dodajemy wykres kołowy i wstawiamy do niego dane
Teraz przyszedł czas na wstawienie wykresu.
Wybierz z palety kontrolek, z sekcji Charts
wykres kołowy (Pie Chart
) i umieść go w centralnej części kontenera. Następnie ustaw dla niego w sekcji Code
kontrolera wartość pola fx:id
na wykres1PieChart
. Zapisz.
Następnie musimy umieścić w nim dane. Zatem trzeba wrócić do edycji pliku FXMLDocumentController.java
. Najpierw dodaj deklarację świeżo dodanego wykresu:
@FXML private PieChart wykres1PieChart;
Obiekt typu PieChart
wyświetla dane, które przekazujemy mu jako listę (observableArrayList
) obiektów typu PieChart.Data
, który przechowuje informację o nazwie danych i ich wartości. Tak się składa, że odpowiada to zawartości obiektów Seria
. Musimy je jednak przekonwertować, w tym celu tworzymy odpowiednią metodę:
1 2 3 4 5 6 7 8 9 10 11 12 |
// metoda, która przyjmuje listę obiektów typu Seria // i zwraca listę obiektów typu PieChart.Data z tymi samymi danymi private ObservableList konwertujDoPieChartData(ObservableList inDat) { ArrayList lista = new ArrayList(); for(Seria s : inDat) { lista.add(new PieChart.Data(s.getNazwa(),s.getSrednia())); } // Tworzymy listę (ObservableList) obiektów typu PieChart.Data // poprzez "opakowanie" listy typu ArrayList ObservableList outData = FXCollections.observableArrayList(lista); return outData; } |
Teraz w metodzie initialize()
umieszczamy kolejną instrukcję, która wstawia dane do wykresu:
wykres1PieChart.dataProperty().set(konwertujDoPieChartData(dane));
Dane zwraca w odpowiedniej formie metoda konwertujDoPieChartData()
.
Zapisujemy plik i uruchamiamy program. Teraz naszym oczom pokazuje się taki obrazek:
Wczytywanie danych pliku i modyfikacje aplikacji
Przyszedł czas na dodanie możliwości wczytania danych z pliku. Najpierw przygotujmy odpowiedni plik w formacie TSV
, czyli dane będą odseparowane tabulatorami. Niech będą do wyniki pierwszej tury wyborów prezydenckich 2015. Dane pochodzą ze strony PKW.
Nazwij plik na przykład wybory.tsv
i zapisz w dogodnym miejscu.
Uwaga: Jeśli poniższe dane skopiujesz i wkleisz do edytora, upewnij się czy dane są oddzielone tabulatorem (pomiędzy imieniem i liczbą głosów). Jeśli znajdują się tam spacje to zmień je na znak tabulatora.
1 2 3 4 5 6 7 8 9 10 11 |
Braun Grzegorz 124132 Duda Andrzej 5179092 Jarubas Adam 238761 Komorowski Bronisław 5031060 Korwin-Mikke Janusz 486084 Kowalski Marian 77630 Kukiz Paweł 3099079 Ogórek Magdalena 353883 Palikot Janus 211242 Tanajno Paweł 29785 Wilk Jacek 68186 |
Teraz trzeba dodać możliwość odczytu pliku. Zacznijmy od dodania odpowiedniej pozycji w menu. Przejdź do SceneBuildera, rozwiń sekcję Menu
w palecie kontrolek a następnie hierarchię kontrolek a w niej rozwiń zawartość menu tak, by był widoczny element Zamknij
. Teraz przeciągnij z palety MenuItem
do drzewka hierarchii tak by znalazł się na tym samym poziomie, ale powyżej elementu Zamknij
. Powinno to wyglądać tak:
Następnie w inspektorze w sekcji Properties
zmień pole Text
z „Unspecified Action” na „Otwórz…” a w sekcji Code
wpisz w pole onAction
: „otworzPlikAction”. Ta ostatnia wartość to oczywiście nazwa metody, która się będzie uruchomiała po wybraniu świeżo dodanej pozycji menu. Trzeba zatem dodać tą metodę do pliku `FXMLDocumentController.java”, na razie jest ona pusta:
1 2 3 |
@FXML private void otworzPlikAction(ActionEvent event) { } |
Dalej, dodajemy deklarację Stage
, będziemy się do niej odwoływać:
@FXML
private Stage stage;
Wypełniamy treścią metodę otworzPlikAction()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@FXML private void otworzPlikAction(ActionEvent event) { // Tworzymy kontrolkę (okienko) służącą do wybierania pliku FileChooser fileChooser = new FileChooser(); // Tytuł okienka fileChooser.setTitle("Otwórz Plik"); // Dodajemy filtr rodzaju pliku - tu plików tsv fileChooser.getExtensionFilters().add( new ExtensionFilter("Pliki TSV", "*.tsv") ); // Pokaż okno File plik = fileChooser.showOpenDialog(stage); // Jeśli zamkniemy fileChooser nie wybierając pliku zostanie zwrócony null // Jeśli wybierzemy plik, podejmujemy odpowiednie działania if (plik != null) { // Wyswietlenie w terminalu ścieżki do pliku. System.out.println("Plik: "+ plik.getAbsolutePath()); } } |
Na razie metoda pozwala wybrać plik, do czego służy okienko dialogowe FileChooser
a także drukuje w terminalu prowadzącą do niego ścieżkę (zwróć uwagę jaka metoda jest wywoływana). Do okna dialogowego dodajemy filtr, który pozwala na selekcję plików możliwych do otworzenia w zależności od przedłużenia nazwy pliku. Tutaj filtr pozwala na wybranie plików, które mają przedłużenie .tsv
ale możesz oczywiście dodać dowolną liczbę takich filtrów dla różnych rodzajów plików. Oczywiście trzeba później zadbać, aby aplikacja mogła je obsłużyć. Okienko dialogowe FileChooser
może także zapisywać pliki. W tym celu należy użyć metody showSaveDialog()
zamiast showOpenDialog()
ale nie będziemy z tej możliwości tutaj korzystać.
Uruchom program, wybierz w menu opcję Otwórz...
, wybierz plik z danymi, który wcześniej utworzyliśmy, w terminalu powinna zostać wypisana ścieżka do pliku. Jeśli wszystko działa czas dopisać kod, który odczyta dane z pliku a następnie umieści je w tabeli i na wykresie.
Konwersja danych odczytanych z pliku i przekazanie ich do kontrolek
O podstawach odczytu plików tekstowych pisałem tutaj, nie będę wiec tego powtarzał.
W pliku FXMLDocumentController.java
umieść nową metodę:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// Metoda odpowiada za pobieranie danych z pliku i ładowanie ich // do tabeli oraz wykresu kołowego private void ladujDane(File file) { Path sciezkaDoPliku = Paths.get(file.getAbsolutePath()); ArrayList odczyt = new ArrayList(); try { odczyt = (ArrayList) Files.readAllLines(sciezkaDoPliku); } catch (IOException ex) { System.out.println("Brak pliku!"); } ArrayList lista = new ArrayList(); for(String l : odczyt) { String[] dt = l.split("\t"); if (dt.length > 1){ lista.add(new PieChart.Data(dt[0], Double.parseDouble(dt[1]))); } } // Lista obserwowalna zawierająca obiekty PieChart.Data ObservableList dane = FXCollections.observableArrayList(lista); wykres1PieChart.dataProperty().set(dane); // Ustawienie danych dla tabeli, wykorzystujemy utworzoną powyżej listę tabela.itemsProperty().setValue(dane); // Powiązanie pierwszej kolumny z polem name obiektu typu PieChart.Data nazwaColumn.setCellValueFactory( new PropertyValueFactory<piechart.data, string="">("name") ); // Powiązanie drugiej kolumny z polem pieValue PieChart.Data sredniaColumn.setCellValueFactory( new PropertyValueFactory<piechart.data, double="">("pieValue") ); } </piechart.data,></piechart.data,> |
Jak widać, metoda ladujDane()
pobiera dane z pliku tekstowego, umieszcza je w obiektach typu PieChart.Data
a te z kolei zbiera w obserwowalnej liście o nazwie dane
. Następnie listę te wykorzystuje zarówno jako źródło danych dla wykresu jak i dla tablicy. Mogliśmy w tym wypadku zrezygnować z obiektów typu Seria
, ponieważ obiekt typu PieChart.Data
przechowuje String
i double
w polach o nazwach odpowiednio name
i pieValue
. Możemy zatem usunąć z projektu plik Seria.java
. Część metody odpowiadająca za przypisywanie danych do tabeli i wykresu odpowiada zawartości metody initialize()
po uwzględnieniu odpowiednich modyfikacji związanych z ujednoliceniem sposobu przechowywania danych. Trzeba także zmodyfikować deklaracje obiektów, usunąć zawartość metody initialize()
oraz metodę konwertujDoPieChartData()
, która też już jest zbędna. Kompletny kod pliku FXMLDocumentController.java
wygląda teraz tak:
FXMLDocumentController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
package danefx; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.chart.PieChart; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Stage; public class FXMLDocumentController implements Initializable { // deklaracje obiektów @FXML private TableView tabela; @FXML private TableColumn<piechart.data, string=""> nazwaColumn; @FXML private TableColumn<piechart.data, double=""> sredniaColumn; @FXML private PieChart wykres1PieChart; @FXML private Stage stage; @FXML private void zamknijAplikacjeAction(ActionEvent event) { Platform.exit(); } @FXML private void otworzPlikAction(ActionEvent event) { // Tworzymy kontrolkę (okienko) służącą do wybierania pliku FileChooser fileChooser = new FileChooser(); // Tytuł okienka fileChooser.setTitle("Otwórz Plik"); // Dodajemy filtr rodzaju pliku - tu plików tsv fileChooser.getExtensionFilters().add( new ExtensionFilter("Pliki TSV", "*.tsv") ); // Pokaż okno File plik = fileChooser.showOpenDialog(stage); // Jeśli zamkniemy fileChooser nie wybierając pliku zostanie zwrócony null // Jeśli wybierzemy plik, podejmujemy odpowiednie działania if (plik != null) { // Wyswietlenie w terminalu ścieżki do pliku. System.out.println("Plik: "+ plik.getAbsolutePath()); ladujDane(plik); } } // Metoda odpowiada za pobieranie danych z pliku i ładowanie ich // do tabeli oraz wykresu kołowego private void ladujDane(File file) { Path sciezkaDoPliku = Paths.get(file.getAbsolutePath()); ArrayList odczyt = new ArrayList(); try { odczyt = (ArrayList) Files.readAllLines(sciezkaDoPliku); } catch (IOException ex) { System.out.println("Brak pliku!"); } ArrayList lista = new ArrayList(); for(String l : odczyt) { String[] dt = l.split("\t"); if (dt.length > 1){ lista.add(new PieChart.Data(dt[0], Double.parseDouble(dt[1]))); } } // Lista obserwowalna zawierająca obiekty PieChart.Data ObservableList dane = FXCollections.observableArrayList(lista); wykres1PieChart.dataProperty().set(dane); // Ustawienie danych dla tabeli, wykorzystujemy utworzoną powyżej listę tabela.itemsProperty().setValue(dane); // Powiązanie pierwszej kolumny z polem name obiektu typu PieChart.Data nazwaColumn.setCellValueFactory( new PropertyValueFactory<piechart.data, string="">("name") ); // Powiązanie drugiej kolumny z polem pieValue PieChart.Data sredniaColumn.setCellValueFactory( new PropertyValueFactory<piechart.data, double="">("pieValue") ); } @Override public void initialize(URL url, ResourceBundle rb) { } } </piechart.data,></piechart.data,></piechart.data,></piechart.data,> |
Modyfikacje interfejsu graficznego
Po uruchomieniu aplikacji i załadowaniu danych ujrzymy takie okienko:
Dane się załadowały, ale teraz widać pewne niedociągnięcia w interfejsie graficznym. Nagłówki tabeli nie odpowiadają obecnym danym, ponadto szerokość kolumn jest stanowczo za wąska. Sam wykres jest co prawda mały, ale powiększając okno aplikacji można go łatwo powiększyć. Zajmijmy się zatem tabelą. Przejdź do SceneBuildera i ustaw wartość szerokości tabeli (sekcja Layout
inspektora, pole pref Width
na 120, a szerokość kolumn na 150 i 80. Nagłówki kolumn zmień na „Kandydat” i „L. głosów”. Warto też ustawić preferowaną szerokość BorderPane
na 900 a wysokość na 600 żeby okno aplikacji było większe. Teraz ustawmy pewne właściwości wykresu (PieChart
) w sekcji Properties
. Title
: „Wyniki I tury wyborów prezydenckich 2014”, Legend Side
: TOP.
Teraz aplikacja po uruchomieniu i załadowaniu danych wygląda tak:
Dużo lepiej, prawda? Zauważ, że klikając na nagłówki kolumn, możesz sortować dane wg. nazwisk kandydatów albo liczby uzyskanych głosów (rosnąco i malejąco).
Warto jeszcze zmienić nazwy obiektów reprezentujących kolumny aby uwzględnić zmiany jakie zaszły podczas rozwoju naszej aplikacji na przykład z sredniaColumn
na liczbaGlosowColumn
, nie ma to oczywiście znaczenia dla działania kodu ale wpływa na jego czytelność. Zostawiam to jednak czytelnikowi.
Na koniec podaję kompletny kod pozostałych plików programu:
DaneFX.java
Ten plik w zasadzie nie został zmieniony.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package danefx; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class DaneFX extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } } |
FXMLDocument
1 2 3 4 5 6 7 8 |
<!--?xml version="1.0" encoding="UTF-8"?--> <!--?import javafx.scene.chart.*?--> <!--?import java.lang.*?--> <!--?import java.util.*?--> <!--?import javafx.scene.*?--> <!--?import javafx.scene.control.*?--> <!--?import javafx.scene.layout.*?--> |
1 |
1 2 3 |
copy + paste and your code doesn’t work
Thanks for your comment. I have just checked the code and it works. However, I realized that the problem could be with the datafile. As I described in the text, data should be separated by tabs, but probably WordPress (or plugin) in some way changed all tabs to spaces (sometimes it do strange things with code), so when data were copied and pasted to text file, the file had bad format. You can change all spaces between names and numbers to tabs (one tab between name and number) but now I corrected page and copy and paste should work, at least on my computer worked ;-).
However if there is other problem with the code, please describe it.
Mnie ciekawi jak z poziomu kontrolera zmienić nazwę kolumn?
Szukałem już wszędzie i nie widzę.
O to chodzi?
kolumna.setText(„Nagłówek”);
Proszę o pomoc- robiła krok po kroku i nie działa przy otwieraniu pliku: Wyskakuje błąd:
Caused by: java.lang.ClassCastException: sun.nio.fs.WindowsPath cannot be cast to javafx.scene.shape.Path
at sample.Controller.ladujDane(Controller.java:54)
at sample.Controller.otworzPlikAction(Controller.java:48)
Kasia, you have to call the riht library :
put (import java.nio.file.Path;) instead of (import javafx.scene.shape.Path;)