6. díl - Upomínač narozenin v JavaFX - Propojení vrstev

Java JavaFX Upomínač narozenin v JavaFX - Propojení vrstev

V minulém dílu seriálu tutoriálů o tvorbě formulářových aplikací v JavaFX jsme dokončili základ logické vrstvy aplikace. Dnes ji propojíme s formulářem a aplikaci tak zprovozníme.

Propojení prezentační a logické vrstvy

Nyní máme dokončenou tzv. prezentační část aplikace (formulář) a logickou část (třídy). Tyto 2 vrstvy se v aplikaci striktně oddělují, jelikož jinak je kód velmi nepřehledný. Nikdy byste neměli provádět výpočty, zápisy do souborů, databáze a podobné věci přímo v kontroleru formuláře! Vždy si vytvoříme třídu, která poskytuje příslušné metody a tuto třídu z kontroleru pouze používáme. Logika zůstane ve třídě. Třída by naopak vůbec neměla vědět o formuláři. Neměla by tedy např. zobrazovat chybové hlášky, ale pouze vyhazovat v případě chyby výjimky. Je potom na formuláři, aby uživateli chybu zobrazil. Právě formulář je ta část aplikace, která s uživatelem komunikuje. Žádná jiná to nedělá.

Pokud vás napadlo, že naše jednoduchá kalkulačka, kterou jsme vytvořili v prvních dílech seriálu, byla návrhově špatně, máte pravdu. Z důvodu jednoduchosti jsme napsali výpočty rovnou do obslužné metody tlačítka. Správně bychom měli mít nějakou třídu, která by výsledky počítala a tu bychom z formuláře pouze volali.

Ukážeme si tedy, jak se to dělá správně.

Propojení prezentace a logiky

Přejdeme do kontroleru formuláře a třídě přidáme privátní atribut typu SpravceOsob, ve kterém rovnou vytvoříme instanci správce:

private SpravceOsob spravceOsob = new SpravceOsob();

Instance správce se vytvoří při vytvoření formuláře a formulář s ní dále bude komunikovat a tak provádět úkony, které si přeje uživatel.

V metodě initialize() nastavíme dnesLabel na aktuální datum a ListView nastavíme položky na ObservableList ze správce osob. Odteď bude ListView zobrazovat obsah ObservableListu a pokud se do listu něco přidá, projeví se to i v ListView. Pokud jsou v listu nějaké osoby, nastavíme vybranou položku ListView na první index.

@Override
public void initialize(URL url, ResourceBundle rb) {
        dnesLabel.setText(Datum.zformatuj(Calendar.getInstance()));
        osobyListView.setItems(spravceOsob.getOsoby());
        if (!spravceOsob.getOsoby().isEmpty())
                osobyListView.getSelectionModel().select(0);
}
Přidávání a mazání osob

Abychom něco konečně také viděli, přejdeme k přidávání osob. Nejprve přejdeme do kódu dialogu OsobaDialog, kde si přidáme privátní atribut typu osoba, ke kterému vygenerujeme getter. Až budeme dialog později zobrazovat, právě odtud si načteme osobu, jejíž údaje uživatel zadal.

private Osoba osoba = null;

public Osoba getOsoba() {
        return osoba;
}

Nyní přejdeme do připravené obslužné metody tlačítka OK, ta se nachází v metodě vytvorScenu().

Abychom mohli ve vnitřní třídě přistupovat k proměnným jmenoTextField a datumTextField, musíme je nejprve označit jako final, případně bychom je také mohli umístit mezi atributy třídy OsobaDialog. Proveďme tuto úpravu:

final TextField jmenoTextField = new TextField();
final TextField datumTextField = new TextField();

Vraťme se do obslužné metody tlačítka OK. Zde se pokusíme vytvořit osobu na základě údajů, které uživatel zadal. Osobu uložíme do privátního atributu. Pokud se něco nepovede, vypíšeme chybu. Jistě jste se již setkali s konstrukcí try-catch, která umožňuje odchytit vyvolané výjimky a nějak na tyto chyby reagovat místo toho, abychom nechali aplikaci spadnout.

tlacitko.setOnAction(new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent e) {
                try {
                        Calendar narozeniny = Datum.naparsuj(datumTextField.getText());
                        osoba = new Osoba(jmenoTextField.getText(), narozeniny);
                        hide();
                } catch (ParseException | IllegalArgumentException ex) {
                        System.out.println("Chyba: " + ex.getMessage());
                }
        }
});

Právě řádek:

osoba = new Osoba(jmenoJTextField.getText(), narozeniny);

Může vyvolat výjimku, jelikož výjimku přímo vyhazujeme v konstruktoru osoby v případě špatných údajů, můžete se tam podívat. Další výjimku může vyvolat i parsování datumu, protože do TextFieldu může uživatel napsat prakticky cokoli a komponenta k výběru data není v JavaFX zatím obsažená. Kód, který výjimku vyvolává, umístíme do bloku try. V bloku catch poté výjimku odchytíme a zobrazíme její zprávu uživateli. Pokud vše proběhne hladce, program se do bloku catch ani nedostane. Výjimky většinou vyvoláváme v logických třídách a potom je chytáme ve formulářích. Výjimky jsou podrobněji popsané v článku Výjimky v Javě. Pomocí hide() zavíráme aktuální formulář.

Jistě jste si všimli, že chybu vypusujeme pouze do konzole. JavaFX zatím neobsahuje podporu chybových hlášek. Nic nám ovšem nebrání hlášku zobrazit jako další formulář. Vy byste již dokázali velmi jednoduše vytvořit formulář s jedním labelem a tlačítkem, který by chybu zobrazil. V tutoriálu jsem toto zanedbal, můžete si projekt později vylepšit.

V tomto formuláři jsme skončili. Přesuneme se zpět do kontroleru a doplníme kód do obslužných metod tlačítek Přidat a Odebrat.

@FXML
public void handlePridatButtonAction(ActionEvent event) {
        OsobaDialog dialog = new OsobaDialog(dnesLabel.getScene().getWindow());
        dialog.showAndWait();

        Osoba nova = dialog.getOsoba();
        if (nova != null)
        {
                spravceOsob.pridej(nova);
        }
}

V obslužné metodě tlačítka pridatButton již vytváříme novou instanci dialogu OsobaDialog. Osobu z dialogu si nyní i načteme a pokud tam nějaká je (uživatel dialog potvrdil tlačítkem OK a neodkřížkoval ho), přidáme osobu do správce. Protože používáme ObservableList, zobrazí se osoba ihned i v ListView na formuláři.

Obslužná metoda tlačítka odebratButton bude vypadat takto:

@FXML
public void handleOdebratButtonAction(ActionEvent event) {
        Osoba vybrana = (Osoba)osobyListView.getSelectionModel().getSelectedItem();
        if (vybrana != null)
        {
                spravceOsob.odeber(vybrana);
        }
}

Důležitá je podmínka, která zjišťuje, zda je v ListView vybraná nějaká položka. Jak vidíte, k vybrané položce se dostaneme přes vlastnost getSelectedItem na SelectionModelu. Položku následně přetypujeme na Osobu, jelikož je typu object (to aby byl ListView univerzální). Tuto osobu předáme metodě odeber na správci, která dále vykoná fyzické odebrání z ObservableListu.

Aplikaci si nyní můžete vyzkoušet, již půjde přidávat a odebírat osoby. Přidané osoby se ihned objeví v ListView díky tomu, že používáme ObservableList. ListView vždy zobrazuje to, co vrací metoda toString() objektu. U osob tedy zobrazuje jejich jméno.

Přidávání a odebírání položek v ListView v JavaFX

Příště doplníme logickou vrstvu aplikace o další metody a tím ji dokončíme.


 

Stáhnout

Staženo 410x (182.07 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (2 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (1)

Avatar
Vojtěch Bargl:

Potřeboval bych pomoct, podle návodu vše OK, ale mám takový problém.
Když jsem si zkoušel návod projít znovu a vytvořit OsobaDialog pomocí formuláře FXML vše v pohodě. Našel jsem problém, když jsem chtěl vytvořit FXML ze třídy FXMLOsobaDialog­Controller i když jsem dal extends Stage a snažil se správně upravit konstruktor tak Netbeans pořád házel chybu. To jsem vyřešil pomocí další třídy (OsobaDialog), která se stala třídou pro vyvolání a nastavení(title, modality, style,...) FXML souboru.
Potom se FXML konečně vytvořilo, ale když sem došel k tomuto kroku tak jsem narazil na problém s nastavením akce na tlačítko přidej( v OsobaDialog) nevím jak mám nastavit výše zmíněnou metodu hide() pro stage. Nevěděl byste někdo co s tím? Díky.¨

jinak ta metoda se ukrývá zde:

tlacitko.setOnAction(new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent e) {
                try {
                        Calendar narozeniny = Datum.naparsuj(datumTextField.getText());
                        osoba = new Osoba(jmenoTextField.getText(), narozeniny);
                        **hide();**
                } catch (ParseException | IllegalArgumentException ex) {
                        System.out.println("Chyba: " + ex.getMessage());
                }
        }
});

Pokud budete chtít rád přiložím projekt.

 
Odpovědět 7.3.2014 17:46
Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Díky, špatně bylo jenom to J, tlačítko se jmenuje pridatButton.

Odpovědět 21.3.2014 17:11
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Martin
Člen
Avatar
Martin:

Chtěl bych se zeptat proč se mi aplikace ve swingu spustí hned ale v FX to trvá třeba deset sekund.

 
Odpovědět 24.9.2014 18:32
Avatar
LukyH
Člen
Avatar
LukyH:

U metody odebratButton píšete o přetypování na typ Object. Toto se domnívám by platilo ještě před vydáním JDK5 kdy se poprvé objevila genericita. A v kontroléru přece máme:

private ListView<Osoba> osobyListView;

. Dále mi není zcela jasné použití klíčového slova final u datumTextField ve třídě OsobaDialog. To označení přece jen zajišťuje chránění příslušné (v tomto případě lokální) proměnné před dalšími změnami. Kód jsem skompiloval v Javě 8 bez jakýchkoliv problémů nebo varování. Při spuštění aplikace jsem zatím také nenarazil na problémy. Jsou snad s tímto ve starších verzích Javy problémy nebo jaké jsou důvody použití této konstrukce? Jinak pěkný článek o JavaFX.

 
Odpovědět  +1 27.12.2014 20:20
Avatar
kavalekp
Člen
Avatar
kavalekp:

Pro ty z vas ktery pouzivaji IntelliJ Idea, je zapotrebi v Project Structure -> Project Settings -> Molules, a nastavit Project language level na 7.0 - Diamonds, ARM, multi-catch etc. jinak catch blok v tlacitko.setO­nAction neprojde.

 
Odpovědět 22.1.2015 11:15
Avatar
roman64
Redaktor
Avatar
roman64:

Mám problém. OsobaDialog:
catch (ParseException | IllegalArgumen­tException ex) Netbeans mi píše že mám odstranit (remove) klauzuly **ParseException ** (import java.text.Par­seException; proveden). Hlásí to jako trvdou chybu se žárovkou. Pak neodchytne, když je chyba na parse. Chyby z Osoba (krátké jmné a budoucí datum odchytne). V čem by mohla být příčina? Celý zdroják :
http://www.itnetwork.cz/dev-lighter/619

Editováno 14.7.2015 14:43
Odpovědět 14.7.2015 14:40
osvícený člověk se učí celý život, hlupákovi stačí hodina
Avatar
Odpovídá na roman64
Petr Štechmüller:

Zkuste nahradit

ParseException | IllegalArgumentException

jenom za obyčejnou vyjímku

Exception

pak by to mělo fungovat určitě...

Odpovědět  +1 14.7.2015 14:49
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
roman64
Redaktor
Avatar
roman64:

Exception zafungoval :-) . Dík. příčina asi bude v použité mětodě parsování datumu. Používám novou třídu LocalDate a tuto notaci: LocalDate datumNarozeniny = LocalDate.par­se(datumNaroze­niTextField.get­Text(), DateTimeFormat­ter.ofLocalized­Date(FormatSty­le.MEDIUM));. To by vysvětlovalo i skutečnost, že v případě otevření "Upominacnaro­zenin4.zip" Netbeans nenutí ParseException odstranit (neb je tam použitá jiná metoda parsování).

Odpovědět 14.7.2015 21:21
osvícený člověk se učí celý život, hlupákovi stačí hodina
Avatar
petr.fiala
Člen
Avatar
petr.fiala:

děkuji za pěkný přehledný tutoriál. Zajímalo by zda jsem správně odpozoroval, že v případě, kdy vytváříme nové okno čistě pomoci Javy (OsobaDialog) v podstatě view a controller spojujeme do jedné třídy?

 
Odpovědět 9. července 15:29
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na petr.fiala
pocitac770:

Nevím, co myslíš v tuto chvíli pojmem "View", ale tím spojením máš víceméně pravdu. FXML je pouhé zjednodušení FX, které umožňuje automaticky vykonat určité kusy kódu (které jsme si ručně definovali na příkladu v OsobaDialog) s pomocí XML souboru, kde jsou použité vlastnosti (velikosti, obsah, propojovací metody) definovány. Sice to tu nebylo ukázáno, ale i s pomocí FXML jde vytvořit více oken najednou propojených logikou, jako bylo u zdejšího příkladu.

Editováno 9. července 15:45
 
Odpovědět 9. července 15:44
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 11. Zobrazit vše