Programovanie (2) v Jave
1-INF-166, LS 2017/18

Úvod · Pravidlá · Prednášky · Netbeans · Testovač · Test a skúška
· Vyučujúcich môžete kontaktovať pomocou e-mailovej adresy E-prg.png (bude odpovedať ten z nás, kto má príslušnú otázku na starosti alebo kto má práve čas).
· DÚ10 je zverejnená, odovzdávajte do stredy 16.5. 22:00.
· Bonusový projekt odovzdávajte do pondelka 21.5. 22:00 na testovači. Predvádzanie projektov bude po prvom termíne skúšky v stredu 23.5. (t.j. začneme medzi 11:45 a 12:00 v H6). Ak vtedy nemôžete prísť, kontaktujte vyučujúce, dohodneme iný termín.
· Opravný/náhradný test bude v pondelok 28.5. o 10:00 v F1-108. V prípade záujmu sa prihláste v AISe.
· Na termíny skúšok sa zapisujte v AIS. Termíny: streda 23.5. (riadny), štvrtok 7.6. (riadny alebo 1. opravný), streda 20.6. (1. alebo 2. opravný) plus v prípade potreby ešte jeden termín v poslednom týždni skúškového. Prípadné konflikty nám dajte vedieť čím skôr. Ďalšie informácie na stránke Test a skúška.


Prednáška 31

Z Programovanie
Prejsť na: navigácia, hľadanie

Prednáška 31

Event driven programming

  • Občas (často) sa program dostáva do situácie, kedy musí čakať na niečo zvonku - kým používateľ zadá vstup, kým niekto niekam klikne ..
  • V zásade sú dva možné prístupy, čo v takejto situácii robiť:
    • Jednou možnosťou je aktívne čakanie na udalosť. Môžeme si to predstaviť ako cyklus while (!udalost) { skontrolujUdalost(); }
      • Možno si spomínate na Oslíka v Shrekovi - presne takto sa chová takýto program.
      • Je to však trochu mrhanie časom a výkonom, najmä u programov, kde sa očakáva časté a dlhé čakanie (napríklad rôzne GUI)
    • Preto sa v takýchto situáciách používa Event driven programming (programovanie riadené udalosťami)
      • Na rozdiel od predchádzajúceho povie program operačnému systému, že si ide pospať a keby sa náhodou niečo stalo, že ho má zobudiť.
      • Spôsobí to však dosť odlišný spôsob programovania, ako sme boli zvyknutí. Program prestáva byť súvislou postupnosťou kódu, ale rozpadá sa na kúsky, ktoré sa (jeden alebo aj viac z nich) vykonajú, keď nastane nejaká udalosť.
      • Viac o tomto spôsobe sa dočítate napr. tu

Ako vlastne fungujú Eventy všeobecne

Event je vlastne akcia, ktorú niekto vyvolá a niekto iný zkonzumuje

  • V prípade kliknutia na gombík tento gombík vyvolá MouseEvent
  • Potrebujeme teda niekoho, kto tento Event spracuje -- obvykle EventHandler čakajúci na konkrétny typ Eventu

Ukážeme si jednoduchšiu (nie JavaFX) aplikáciu, kde Eventy využijeme

  • Budeme mať vlastný nový typ Eventu, ktorý bude vedieť zdroj (kto ho vyvolal) a ešte String what
    • Konštruktory objektu volajú všeobecný Event
    • Okrem toho vie Event oznámiť aj String what
class myEv extends Event{
    private String what;
    public myEv(Object source){ 
        super(source,NULL_SOURCE_TARGET,new EventType("myEv"));      
    }
    myEv(Object source, String s){ super(source,NULL_SOURCE_TARGET,new EventType("myEv")); what=s;}
    public String getWhat(){ return what; }
}
  • Na spracovávanie Eventov si vytvoríme triedu, ktorá implementuje všeobecný EventHandler na náš nový typ Eventov
    • EventHandler potrebuje implementovať jedinú metódu handle s parametrom -- Event typu ktoré má spracovávať
    • Vidíme, že môžeme využívať špecifické metódy myEv (getWhat) ako aj metódy podedené od Eventu (getSource)
class myHandler implements EventHandler<myEv> {
    @Override public void handle(myEv ev) {
        System.out.println("I handle event: "+ ev.getWhat()+ " from "+ ((mySender)ev.getSource()).getName());
    } 
}
  • Keď už máme Eventy aj niekoho, kto ich vie spracovať, potrebujeme ich ešte na niekom vytvárať (ako je napr. gombík) -- v našom prípade mySender
    • Ten niekto by mal vedieť robiť setOnAction (aby sme vedeli pridať niekoho čakajúceho na akciu)
    • Okrem toho musí vlastne aj spôsobovať akciu -- makeAction (toto v JavaFX robí za nás aplikácia resp. používateľ, ktorý kliká myšou, navyše je to spracovávanie výrazne zložitejšie)
    • Okrem toho si samotný Sender bude pamätať tých, ktorým oznámi, keď je nejaká akcia, počas ktorej vznikne Event (tu makeAction)
class mySender {
    private String name;
    private ArrayList<EventHandler<myEv>> myActionListeners=new ArrayList<>();
    
    mySender(String s){ name=s; }
    public String getName(){
        return name;
    }
    void setOnAction(EventHandler<myEv> h){
        myActionListeners.add(h);
    }
    void makeAction(){
        myEv ev= new myEv(this, "Hello");
        for (EventHandler<myEv> h: myActionListeners){
            h.handle(ev);
        }
    }
}
  • Teraz môžeme celé spracovávanie Eventov spustiť
    • V main si vytvoríme Sendera, pridáme mu niekoho, kto na neho čaká a vyvoláme na ňom akciu
    • Prípadne môžeme Handlera vytvoriť aj ako anonymnú triedu, prípadne ich vytvoriť viac...
public static void main(String[] args) {
        mySender A=new mySender("A");
        A.setOnAction(new myHandler());
        A.setOnAction(new EventHandler<myEv>(){
            @Override
            public void handle(myEv ev) {                
                System.out.println("I also handle event: "+ ev.getWhat());
            }
        });
        A.makeAction();
    }
  • Vidíme, že sa to podobá na kód, ktorý pri vytvárame pri JavaFX aplikácii
    • Button je vlastne náš Sender -- má svoje meno, vieme na ňom zavolať metódy setOn..., ktoré majú ako parameter niekoho, kto vie spracovať udalosť daného typu
    • Jediný rozdiel je, že vyvolanie akcie musíme urobiť my
  • Ešte sme nezapojili poslednú zaujímavú vlastnosť Eventov, ich konzumáciu
    • Event má metódy consume() a isConsumed(), ktoré určujú, či ešte udalosť existuje a potrebené ju ďalej spracovávať
    • V našom príklade si môžeme túto skutočnosť odsimulovať v metóde makeAction, kde cyklus ukončíme keď nám udalosť niekto zkonzumuje
    • A zabezpečíme, že ju niekto zkonzumuje (myHandler v metóde handle zavolá ev.consume())
    • Vidíme, že k druhému čakateľovi na udalosť sa už táto udalosť nedostane (opäť je to v JavaFX zložitejšie)
class myHandler implements EventHandler<myEv> {
    @Override public void handle(myEv ev) {
        System.out.println("I handle event: "+ ev.getWhat()+ " from "+ ((mySender)ev.getSource()).getName());
        ev.consume();
    } 
}

class mySender {
...
    void makeAction(){
        myEv ev= new myEv(this, "Hello");
        for (EventHandler<myEv> h: myActionListeners){
            h.handle(ev);
            //if (ev.isConsumed()) break;
        }
    }
}

EventHandlery v JavaFX

  • Doteraz sme akciu na ktorú má prvok reagovať zapisovali nasledovne:
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent event) {
        System.out.println("Hello World!");
    }
});

Pozrime sa teraz poriadnejšie na to, čo to vlastne znamená

  • Akciu, ktorá sa vykonala na gombíku (v tomto prípade) má niekto spracovať. Ten niekto je nejaký EventHandler, ktorý má typový parameter ActionEvent (teda vie spracovávať ActionEvent)
  • Mohli by sme to teda zapísať nasledovne (až na to, že trieda EventHandler<> je abstraktná a teda nemá implementované niektoré svoje metódy)
EventHandler<ActionEvent> niekto= new EventHandler<ActionEvent>(); 
btn.setOnAction(niekto);
  • Takže na to, aby sme mohli vytvoriť niekoho typu EventHandler<> , musíme doimplementovať niektoré metódy (v našom prípade metódu handle(ActionEvent event)
EventHandler<ActionEvent> niekto= new EventHandler<ActionEvent>(){
    @Override public void handle(ActionEvent event) {
        ...    
    }        
};
btn2.setOnAction(niekto);
  • Vytvorili sme tým vlastne anonymnú triedu a jej inštanciu.
  • Ďalšia možnosť je, že túto triedu budeme používať častejšie - viacero prvkov bude mať podobnú reakciu, ale závislú napríklad od typu prvku alebo inej okolnosti. Potom môžeme vytvoriť triedu konkrétnu a použiť ju (či už jej anonymnú alebo pomenovanú inštanciu).
class PrintIt implements EventHandler<ActionEvent> {
    String s;
    PrintIt(String str) { s = str; }
    @Override public void handle(ActionEvent ev) { System.out.println(s); }
}

...

btn1.setOnAction(new PrintIt("Hello")); // anonymná inštancia

PrintIt niekto=new PrintIt("Hello");    // pomenovaná inštancia
btn2.setOnAction(niekto);
  • Ako parameter môžeme konštruktoru triedy PrintIt dať aj napr. objekt, ktorý ho zavolá (alebo vhodnú nadtriedu, aby zahŕňala všetky potenciálne objekty), ktoré ju môžu skúšať volať (napr. trieda Node)
  • Trochu väčší problém je v prípade, kedy chceme nastaviť viacero akcii na ten istý prvok a majú všetci reagovať na tú istú udalosť. V našom neJavaFXovom príklade nám tomu nič nebránilo. Skúsme teda spraviť to isté. Čo sa stané, ak klikneme teraz na gombík btn?
btn.setOnAction((ActionEvent event) -> { System.out.println("ahoj"); }); 
btn.setOnAction((ActionEvent event) -> { System.out.println("hello"); }); 
  • Ako vidíme, vykonala sa iba jedna z akcií (tá posledne pridaná). Ak by sme chceli aby sa vykonali obe, potrebujeme minimálne namiesto druhého setOnAction (kľudne môžeme aj obidvom) použiť metódu addEventHandler nasledovne
btn.setOnAction((ActionEvent event) -> { System.out.println("ahoj"); }); 
btn.addEventHandler(ActionEvent.ACTION, (ActionEvent event) -> { System.out.println("hello"); }); 

Posielanie Eventov v JavaFX aplikácii

  • Pozrieme sa, čo sa stane, ak na obrazovke máme viacero prvkov, ktoré môžu chcieť spracovať jednu udalosť.
  • Na začiatok si vezmeme jeden obdlždnik na paneli. Oba sú schopné spracovať udalosť typu MouseEvent.
    • Prvý udalosť dostane obdĺždnik a spracuje ju.
    • Ak tento udalosť nezkonzumoval, dostane sa aj k panelu, ktorý ju tiež môže spracovať.
  • Zaujímavejšie to začne byť, keď na paneli máme dva obdlždniky. Pokiaľ sa neprekrývajú nič sa nemení. Takže si vytvoríme dva, ktoré sa prekrývajú.
@Override public void start(Stage primaryStage) {
    Pane root = new Pane();
    Scene scene = new Scene(root, 300, 250);

    Rectangle r1=new Rectangle();
    r1.setX(0); r1.setY(0); r1.setWidth(100); r1.setHeight(100); r1.setFill(Color.BLUE);
    r1.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            System.out.println("R1");
        }
    });
        
    Rectangle r2=new Rectangle();
    r2.setX(50); r2.setY(50); r2.setWidth(100); r2.setHeight(100); r2.setFill(Color.RED);
    r2.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            System.out.println("R2");
        }
    });
        

    root.getChildren().addAll(r1,r2);
    root.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            System.out.println("Root");
        }
    });

    primaryStage.setScene(scene);
    primaryStage.show();
}
  • Vidíme, že ak objekt je na jednej úrovni s niekým ďalším tak udalosť dostane iba ten, koho je vidno (to je aj vysvetlenie, prečo to tak fungovalo pre 2 gombíky cez seba).
  • Špeciálnym prípadom je však panel. Keď na základaný panel (v našom prípade root) umiestnime ďalší prázdny panel typu Pane, zistíme, že jeho reakcia na udalosť sa nikdy nezavolá. Je to preto, že tento panel (keďže je prázdny) má nulovú veľkosť. Umiestnime naň teda obdĺždnik.
...
    Rectangle r3=new Rectangle();
    r3.setX(70); r3.setY(70); r3.setWidth(50); r3.setHeight(50); r3.setFill(Color.YELLOW);
    r3.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            System.out.println("R3");
        }
    });

    Pane panel=new Pane();
    panel.getChildren().add(r3);
    panel.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
             System.out.println("Panel");
        }
    });

    root.getChildren().addAll(r1,r2,panel);
... 
  • Panel má teraz veľkosť po pravý dolný roh r3. Môžeme vyskúšať ktoré spracovanie udalosti kde nastane.
  • Vyskúšajte si, čo sa stane, keď jednotlivé objekty udalosť zkonzumujú. A čo keď niekto udalosť spracovávať nebude?

Zhrnutie

Ako teda funguje spracovanie udalostí?

  • Najprv sa zistí, komu bola asi udalosť určená. Bude to objekt, ktorý je na obrazovke reálne vidieť (t.j. je úplne navrchu v mieste, kde udalosť vznikla).
  • Od tohto objektu sa zkonštruuje cesta, koho všetkého môže udalosť zaujímať. Prvý je samotný "zastihnutý" objekt O, potom objekt, medzi ktorého deti O patrí, atď.
  • Po tejto ceste sa bude udalosť pohybovať a spracovávať (ak ju jednotlivé objekty na ceste chcú), kým nebude zkonzumovaná alebo nedosiahne primaryStage.
  • Do tohto procesu môžu ešte vstupúovať filtre, ale o tých sa v tomto krátkom prehľade o JaveFX venovať nebudeme.

Dizajn aplikácie

  • Keď chceme, aby aplikácia nemala iba napevno nastavenú veľkosť okna a na čomkoľvek inom vyzerala strašne, nemôžeme súradnice prvkov nastavovať napevno, ale musíme im dať nejaké pravidlá
  • Na to slúžia rôzne panely, ktoré prvky umiestňujú
  • Tieto panely je bežne vhodné kombinovať a až na túto "vrstvu" panelov umiestniť konkrétne ovládacie prvky

Layouty

  • Pane - všeobecný panel, kde môžeme prvkom nastaviť súradnice
  • BorderPane - bežne sa používa ako základný panel, prvky sa rozmiestňujú do 5 regiónov (center, top, bottom, left, right)
  • HBox/VBox - umiestňuje prvky horizontálne resp. vertikálne (za seba resp. pod seba)
  • StackPane - umiestňuje prvky na seba (vhodné, keď chceme vytvoriť štvorček s popisom s grafikou a ešte niečím)
  • GridPane - umiestňuje prvky do mriežky (t.j. pri pridaní prvku povieme do ktorého políčka mriežky prvok pridať)
  • FlowPane - umiestňuje prvky za seba tak, aby sa zmestili do priestoru vuhradenému panelu (prvky pri zmene veľkosti panelu môžu plávať)
  • TilePane - niečo medzi FlowPane a GridPane - umiestňuje do mriežky ale postupne (môžeme nastaviť preferovaný počet riadkov/stĺpcov)
  • AnchorPane - umožňuje prvky ukotviť na určitú pozíciu na paneli (napr. 10px od okraja a pod.)
@Override public void start(Stage primaryStage) {        
    BorderPane root = new BorderPane();        
    HBox hpanel= new HBox();    
    hpanel.setStyle("-fx-background-color: black;");
    VBox vpanel=new VBox();
    vpanel.setStyle("-fx-background-color: green;");
    GridPane gridpanel=new GridPane();
    gridpanel.setStyle("-fx-background-color: blue;");
    FlowPane flowpanel=new FlowPane();
    flowpanel.setStyle("-fx-background-color: red;");
    AnchorPane anchorpanel=new AnchorPane();
    anchorpanel.setStyle("-fx-background-color: yellow;");

    root.setCenter(flowpanel);
    root.setTop(hpanel);
    root.setLeft(vpanel);
    root.setRight(gridpanel);
    root.setBottom(anchorpanel);

    Button btn1=new Button("1");
    btn1.setMinHeight(50);
    Button btn2=new Button("2");
    Button btn3=new Button("3");
    btn3.setMinHeight(40);
    btn3.setMinWidth(150);
    Button btn4=new Button("4");
    Button btn5=new Button("5");
    Button btn6=new Button("6");
    Button btn7=new Button("7");
    btn7.setMinSize(40,40);
    Button btn8=new Button("8");
    btn8.setMinSize(50, 50);
    Button btn9=new Button("9");
    btn9.setMinWidth(100);
    Button btn10=new Button("10");
    btn10.setMinSize(300, 30);
    Button btn11=new Button("11");

    hpanel.getChildren().addAll(btn1,btn2);
    vpanel.getChildren().addAll(btn3,btn4,btn5);
    gridpanel.add(btn6, 1,0);
    gridpanel.add(btn7, 0,1);
    flowpanel.getChildren().addAll(btn8,btn9,btn10);
    anchorpanel.getChildren().add(btn11);
    AnchorPane.setLeftAnchor(btn11, 10.0);
    AnchorPane.setTopAnchor(btn11, 10.0);
    Scene scene = new Scene(root);

    primaryStage.setTitle("Layouts");
    primaryStage.setScene(scene);
    primaryStage.show();
}

Použitie statických metód v layoutoch (a inde)

  • Pri používaní AnchorPane (ale je to možné aj v iných paneloch, ale nebolo to potrebné) sme použili zaujímavú vec
AnchorPane.setLeftAnchor(btn11, 10.0);
  • V podstate, keď si to uvedomíme, my nepracujeme so žiadnym konkrétnym AnchorPane, ale napriek tomu nastavujeme gombíku nejakú jeho vlastnosť na nejakom paneli
    • jedná sa o statickú metódu triedy AnchorPane, ktorá nastaví gombíku nejakú vlastnosť, ktorú pri umiestnení na AnchorPane bude využívať
    • keby sme rovnakú vlastnosť nastavili gombíku, ktorý napokon neumiestnime na AnchorPane, nič sa nestane, lebo túto vlastnosť si nikto iný nevyžiada
    • podobne pre GridPane existujú metódy, ktoré nastavia gombíku preferovaný stĺpec bez toho, aby sme vedeli, na ktorom konkrétnom paneli sa nachádza
  • Statické metódy sme videli (okrem využívania na začiatku semestra, kedy sme ich používali aby sme sa vyhli objektom) napríklad pri parsovaní zo stringu
    • Integer.parseInt(s) - vidíme, že sa jedná o metódu triedy Integer

Zhrnutie základov JavyFX

  • Ako príklad, čo všetko už s našimi znalosťami dokážeme vytvoriť je aplikácia na vykresľovanie grafických objektov a práca s nimi
  • Už vieme, že trieda Node obsahuje aj podtriedy pre geometrické útvary
  • Nič nám však nebráni urobiť si vlastný objekt, ktorý bude rozšírením niektorého už existujúceho objektu. Napríklad červený štvorček.
  • Štvorčeky potom môžeme jednoducho vytvárať a umiestňovať na panel.
class MyRedSquare extends Rectangle {
    MyRedSquare(){ this(10); }
    MyRedSquare(int size){
        super();
        this.setFill(Color.RED);
        this.setHeight(size);
        this.setWidth(size);
    }
    MyRedSquare(int size, int x, int y){
        this(size);
        this.setX(x);
        this.setY(y);
    }
}

Práca s vykreslenými objektami

  • Aby sme vedeli s vykreslenými objektami nejako pracovať potrebujeme im pridať akcie. Objekty typu Shape majú zdedené od Node niekoľko udalostí na prácu s myšou.
  • Môžeme teda štvorčeku nastaviť, že ak na ňom klikneme myšou, zväčší sa
MyRedSquare square=new MyRedSquare(10,10,10);

square.setOnMouseClicked(new EventHandler<MouseEvent>(){
    @Override public void handle(MouseEvent event) {
         square.setHeight(square.getHeight()+10);
         square.setWidth(square.getWidth()+10);
    }
});
  • Samozrejme takto môžeme štvorčeku nastaviť viacero reakcií na rôzne udalosti.
  • Keďže už máme vlastnú triedu štvorček, môže sa nám hodiť, ak funkciu na zmenu štvorčeka po kliknutí urobíme jednou jeho vlastnou metódou -- napríklad changeSquare()
    • Okrem toho sme štvorčeku pridali aj farbu, ktorú pri každom kliknutí meníme (striedame červenú a modrú)
    • Potom pri odlišnej reakcii zmeníme iba metódu changeSquare
class MyRedSquare extends Rectangle {
    Color col=Color.RED;
    MyRedSquare(){ this(10); }
    MyRedSquare(int size){
        super();
        this.setFill(col); this.setHeight(size); this.setWidth(size);
    }
    MyRedSquare(int size, int x, int y){
        this(size);
        this.setX(x);
        this.setY(y);
    }
    public void changeSquare(){
        this.setHeight(this.getHeight()+10);
        this.setWidth(this.getWidth()+10);
        if (col==Color.RED) col=Color.BLUE;
        else col=Color.RED;
        this.setFill(col);
    }
}

...
MyRedSquare square=new MyRedSquare(10,10,10);
       
square.setOnMouseClicked(new EventHandler<MouseEvent>(){
    @Override public void handle(MouseEvent event) { square.changeSquare(); }
});
...
  • Skúsime pridať do programu ďalšiu udalosť -- ak klikneme na plochu mimo štvorčeka, štvorček sa presunie na danú pozíciu.
  • Zjavne udalosť kliknutia by mala teda vedieť spracovávať aj Pane.
    • Pridáme teda pane.setOnMouseClicked() s príslušnou reakciou na kliknutie na plochu
    • Čo sa teraz stane, keď klikneme na plochu?
    • A čo sa stane keď klikneme na štvorček? Vidíme, že sa štvorček aj posunul a teda, že udalosť sa dostala až k panelu. Potrebujeme udalosť zastaviť!
  • Výsledná metóda start teda bude vyzerať nasledovne:
@Override public void start(Stage primaryStage) {
    Pane pane = new Pane();
       
    MyRedSquare square=new MyRedSquare(10,10,10);
    square.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            square.changeSquare();
            event.consume();
        }
    });
    pane.setOnMouseClicked(new EventHandler<MouseEvent>(){
        @Override public void handle(MouseEvent event) {
            square.setX(event.getX());
            square.setY(event.getY());
        }
    });
    pane.getChildren().add(square);
        
    Scene scene = new Scene(pane, 300, 250);
    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();
}

Viacero vykreslených objektov

  • Môžeme si vytvoriť celé pole červených štvorčekov a všetky ich vykresliť na panel.
    • K tomu potrebujeme ArrayList<MyRedSquare> squares=new ArrayList<>();
    • Každý štvorček vytvoriť for (int i=0; i<10; i++){ squares.add(new MyRedSquare(10,20*i,20*i));}
    • Každý štvorček pridať na panel for (int i=0; i<10; i++){ pane.getChildren().add(squares.get(i));}
    • Každému štvorčeku pridať spracovávanie udalosti. Alebo spracovávanie udalosti pridať priamo v konštruktore triedy MyRedSquare
  • Samozrejme v prípade, že máme objektov viac ja potrebné pri akcii myši rozmýšľať, ktorého objektu sa akcia má týkať.
    • Toto za nás rieši to, že na udalosť čaká každý z objektov
  • Tiež však potrebujeme rozhodovať, ktorý objekt sa posunie, ak sa akcia vykoná na paneli.
  • Jednoduchým rozšírením vieme napríklad dosiahnuť iba jeden (alebo žiadny) vybraný prvok, s ktorým môžeme potom niečo robiť (vypísať jeho vlastnosti, vymazať...

Vlastné dialógy

  • Pre zložité dialógy si môžeme vytvoriť vlastný Stage, na ktorý umiestnime scénu, panel a pracujeme s ním ako s úvodným oknom aplikácie
  • Samozrejme si môžeme Stage aj rozšíriť a vytvoriť si vlastnú triedu, ktorá vie niečo naviac (napríklad má nejaké premenné navyše)
  • Doležité je nezabudnúť nastaviť Modalitu a vedieť sa vyrovnať s tým, že používateľ mohol okno zavrieť bez zadania (korektného) výsledku
Stage dialog = new Stage();
dialog.initStyle(StageStyle.UTILITY);
dialog.initModality(Modality.APPLICATION_MODAL);
TilePane pan=new TilePane();
Scene sc = new Scene(pan);

TextField rText= new TextField();
TextField gText= new TextField();
TextField bText= new TextField();
Label rLabel=new Label("Red 0.0-1.0: ");
Label gLabel=new Label("Green 0.0-1.0: ");
Label bLabel=new Label("Blue 0.0-1.0: ");
Button ok=new Button("OK");

ok.setOnAction(new EventHandler<ActionEvent>(){
    @Override
    public void handle(ActionEvent event) {
        try {
            double r =Double.parseDouble(rText.getText());
            double g =Double.parseDouble(gText.getText());
            double b =Double.parseDouble(bText.getText());
            if (r>=0 && g>=0 && b>=0 && r<=1 && g<=1 && b<=1){
                rect.setFill(new Color(r,g,b,1));
                dialog.close();    
            }
        }
        catch (Exception e){
            System.err.println("ERR:NekorektnaFarba");
        }                        
    }
});

pan.setPrefColumns(2);
pan.getChildren().addAll(rLabel,rText,gLabel, gText,bLabel,bText,ok);
dialog.setScene(sc);
dialog.showAndWait();

Program vykresľovanie

package javafxapplication1;

import java.util.ArrayList;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class JavaFXApplication1 extends Application {

    MyRedSquare current=null;

class MyRedSquare extends Rectangle {
    Color col=Color.RED;
    
    MyRedSquare(double size){
        super();
        this.setFill(col);
        this.setHeight(size); this.setWidth(size);
        MyRedSquare sq=this;
        setOnMouseClicked(new EventHandler<MouseEvent>(){
           @Override
           public void handle(MouseEvent event) {
               sq.setCurrent();
               event.consume();
           }
        });
    }
    
    MyRedSquare(){ this(10); }
    MyRedSquare(double size, double x, double y){ this(size); this.setX(x);  this.setY(y); }
    
    public void changeColor(Color c){
       col=c;  this.setFill(col); 
    }
    
    public void setCurrent(){
        if (current!=null) { current.changeColor(Color.RED); }
        
        if (current==this) { current=null; }
        else { 
            this.changeColor(Color.BLUE);
            current=this;
        }        
    }
}
    
    @Override
    public void start(Stage primaryStage) {
        
        ArrayList<MyRedSquare> squares=new ArrayList<>();
       
        Pane pane = new Pane();
        pane.setOnMouseClicked(new EventHandler<MouseEvent>(){
            @Override
            public void handle(MouseEvent event) {
                if (current!=null){
                    current.setX(event.getX());
                    current.setY(event.getY());
                }
                else {
                   MyRedSquare newsq=new MyRedSquare(10,event.getX(),event.getY());
                   squares.add(newsq);
                   pane.getChildren().add(newsq);
                   newsq.setCurrent();
                }
            }
        });
       
        Scene scene = new Scene(pane, 300, 250);
        primaryStage.setTitle("My Squares");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }    
}