Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
laboratoare:laborator09 [2015/12/17 21:54] Andrei Roșu-Cojocaru |
laboratoare:laborator09 [2015/12/28 15:52] (current) Andrei Roșu-Cojocaru |
||
|---|---|---|---|
| Line 7: | Line 7: | ||
| * identificarea principalelor componente din cadrul arhitecturii JavaFX; | * identificarea principalelor componente din cadrul arhitecturii JavaFX; | ||
| * descrierea structurii generale a unei aplicații JavaFX și a firelor de execuție din contextul cărora pot fi accesate obiectele corespunzătoare controalelor grafice, respectiv interacțiunii cu utilizatorul; | * descrierea structurii generale a unei aplicații JavaFX și a firelor de execuție din contextul cărora pot fi accesate obiectele corespunzătoare controalelor grafice, respectiv interacțiunii cu utilizatorul; | ||
| - | * implementarea unei interfețe cu utilizatorul folosind Scene Builder 2.0 / FXML cu injectarea controalelor grafice / metodelor de tratare a evenimentelor în clasa controller din codul sursă; | + | * implementarea unei interfețe cu utilizatorul folosind Scene Builder / FXML cu injectarea controalelor grafice / metodelor de tratare a evenimentelor în clasa controller din codul sursă; |
| * înțelegerea mecanismului de tratare a evenimentelor legate de interacțiunea cu utilizatorul în JavaFX; dezvoltarea de filtre și metode de tratare pentru diferite acțiuni ale utilizatorului; | * înțelegerea mecanismului de tratare a evenimentelor legate de interacțiunea cu utilizatorul în JavaFX; dezvoltarea de filtre și metode de tratare pentru diferite acțiuni ale utilizatorului; | ||
| * însușirea practicilor utilizate pentru dezvoltarea aplicațiilor JavaFX. | * însușirea practicilor utilizate pentru dezvoltarea aplicațiilor JavaFX. | ||
| Line 28: | Line 28: | ||
| * **integrare cu Java**. JavaFX este o interfaţă de programare integrată în distribuţia limbajului de programare Java, instalarea sa făcându-se împreună cu acesta; prin urmare, din JavaFX pot fi accesate toate API-urile Java cu funcţionalităţile lor, acesta reprezentând o alternativă la celelalte limbaje de programare care folosesc Java Virtual Machine, precum JRuby sau Scala; | * **integrare cu Java**. JavaFX este o interfaţă de programare integrată în distribuţia limbajului de programare Java, instalarea sa făcându-se împreună cu acesta; prin urmare, din JavaFX pot fi accesate toate API-urile Java cu funcţionalităţile lor, acesta reprezentând o alternativă la celelalte limbaje de programare care folosesc Java Virtual Machine, precum JRuby sau Scala; | ||
| * **limbajul de adnotare FXML şi utilitarul SceneBuilder**. FXML este un limbaj declarativ de adnotare bazat pe XML prin intermediul căruia pot fi dezvoltate interfeţe grafice cu utilizatorul, fără a fi necesar ca aplicaţia să fie recompilată de fiecare dată când sunt modificate elemente din cadrul acesteia. În acest mod se realizează o separare între nivelul de prezentare și nivelul de logică a aplicației. SceneBuilder permite construirea interfeţei în mod vizual, generând automat şi documentul FXML asociat, acesta putând fi integrat apoi în orice mediu de dezvoltare. Astfel, nu mai este necesară decât implementarea mecanismelor de tratare a evenimentelor corespunzătoare diferitelor controale (elemente din cadrul interfeței grafice); | * **limbajul de adnotare FXML şi utilitarul SceneBuilder**. FXML este un limbaj declarativ de adnotare bazat pe XML prin intermediul căruia pot fi dezvoltate interfeţe grafice cu utilizatorul, fără a fi necesar ca aplicaţia să fie recompilată de fiecare dată când sunt modificate elemente din cadrul acesteia. În acest mod se realizează o separare între nivelul de prezentare și nivelul de logică a aplicației. SceneBuilder permite construirea interfeţei în mod vizual, generând automat şi documentul FXML asociat, acesta putând fi integrat apoi în orice mediu de dezvoltare. Astfel, nu mai este necesară decât implementarea mecanismelor de tratare a evenimentelor corespunzătoare diferitelor controale (elemente din cadrul interfeței grafice); | ||
| - | * **componente WebView**. Un modul ce foloseşte tehnologia WebKitHTML permite integrarea de pagini Internet în cadrul unei interfeţe grafice. De asemenea, codul JavaScript ce rulează în WebView poate accesa orice API-uri Java, la fel cum API-urile Java pot executa cod JavaScript care rulează în WebView. Există suport și pentru anumite funcționalități HTML5 (Web Sockets, Web Workers, Web Fonts) sau capabilități de tipărire; | + | * **componente WebView**. Un modul ce foloseşte tehnologia WebKitHTML permite integrarea de pagini Internet în cadrul unei interfeţe grafice. De asemenea, codul JavaScript ce rulează în [[https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebView.html|WebView]] poate accesa orice API-uri Java, la fel cum API-urile Java pot executa cod JavaScript care rulează în WebView. Există suport și pentru anumite funcționalități HTML5 (Web Sockets, Web Workers, Web Fonts) sau capabilități de tipărire; |
| - | * **interoperabilitate cu tehnologia Swing**. Aplicaţiile Swing existente pot fi extinse cu funcţionalităţi JavaFX, ca redarea de conţinuturi grafice sau a unor pagini Internet integrate. Clasa ''SwingNode'' permite integrarea unui conținut dezvoltat folosind tehnologia Swing în cadrul aplicației JavaFX; | + | * **interoperabilitate cu tehnologia Swing**. Aplicaţiile Swing existente pot fi extinse cu funcţionalităţi JavaFX, ca redarea de conţinuturi grafice sau a unor pagini Internet integrate. Clasa [[https://docs.oracle.com/javase/8/javafx/api/javafx/embed/swing/SwingNode.html|SwingNode]] permite integrarea unui conținut dezvoltat folosind tehnologia Swing în cadrul aplicației JavaFX; |
| - | * **biblioteci de controale şi foi de stil**. JavaFX pune la dispoziţia programatorilor majoritatea controalelor necesare pentru dezvoltarea unei interfeţe cu utilizatorul complete (inclusiv ''DatePicker'' și ''TreeTableView''), aspectul acestora putând fi particularizat folosind tehnologii web standard ca foile de stil (//eng.// CSS – cascading style sheets), prin intermediul claselor ''Styleable*''; | + | * **biblioteci de controale şi foi de stil**. JavaFX pune la dispoziţia programatorilor majoritatea controalelor necesare pentru dezvoltarea unei interfeţe cu utilizatorul complete (inclusiv [[https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/DatePicker.html|DatePicker]] și [[https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeTableView.html|TreeTableView]]), aspectul acestora putând fi particularizat folosind tehnologii web standard ca foile de stil (//eng.// CSS – cascading style sheets), prin intermediul claselor ''Styleable*''; |
| * **tema Modena** este folosită ca implicită pentru toate aplicațiile JavaFX (înlocuind tema Caspian, care poate fi utilizată prin apelul metodei ''setUserAgentStylesheet(STYLESHEET_CASPIAN)'' în metoda ''start()'' a clasei derivate din ''Application''; | * **tema Modena** este folosită ca implicită pentru toate aplicațiile JavaFX (înlocuind tema Caspian, care poate fi utilizată prin apelul metodei ''setUserAgentStylesheet(STYLESHEET_CASPIAN)'' în metoda ''start()'' a clasei derivate din ''Application''; | ||
| * **funcționalități de grafică 3D**. Biblioteca de grafică 3D a fost îmbogățită prin: | * **funcționalități de grafică 3D**. Biblioteca de grafică 3D a fost îmbogățită prin: | ||
| Line 62: | Line 62: | ||
| Platforma JavaFX dispune de o arhitectură bazată pe o stivă de componente (transparentă pentru programator), constituind motorul ce rulează codul propriu-zis (Quantum Toolkit). Acesta cuprinde un motor grafic performant (Prism), un eficient sistem de ferestre, de dimensiuni mici (Glass), un motor pentru redarea conţinutului multimedia şi un motor pentru integrarea conţinutului Internet. | Platforma JavaFX dispune de o arhitectură bazată pe o stivă de componente (transparentă pentru programator), constituind motorul ce rulează codul propriu-zis (Quantum Toolkit). Acesta cuprinde un motor grafic performant (Prism), un eficient sistem de ferestre, de dimensiuni mici (Glass), un motor pentru redarea conţinutului multimedia şi un motor pentru integrarea conţinutului Internet. | ||
| - | {{ :laboratoare:laborator03:figura01.png?nolink }} | + | {{ :laboratoare:laborator09:01javafxarchitecture.png?nolink }} |
| ==== Setul de API-uri Java pentru funcționalități JavaFX ==== | ==== Setul de API-uri Java pentru funcționalități JavaFX ==== | ||
| Line 200: | Line 200: | ||
| * ''javafx.scene.Scene'' - are asociat graful de noduri menţinând un model intern al elementelor grafice din cadrul aplicaţiei; la fiecare moment de timp, acesta ştie ce obiecte să afişeze, care zone ale ecranului trebuie actualizate şi cum să realizeze acest proces într-un mod eficient; metodele de desenare nu sunt apelate manual de utilizator, ci automat de sistemul de randare, conform informaţiilor reţinute în graful de noduri. | * ''javafx.scene.Scene'' - are asociat graful de noduri menţinând un model intern al elementelor grafice din cadrul aplicaţiei; la fiecare moment de timp, acesta ştie ce obiecte să afişeze, care zone ale ecranului trebuie actualizate şi cum să realizeze acest proces într-un mod eficient; metodele de desenare nu sunt apelate manual de utilizator, ci automat de sistemul de randare, conform informaţiilor reţinute în graful de noduri. | ||
| - | {{ :laboratoare:laborator03:figura02.png?nolink&500 }} | + | {{ :laboratoare:laborator09:02applicationstructure.png?nolink&500 }} |
| Fiecare element din cadrul unei scene este exprimat sub forma unui nod dintr-un graf (arbore) în care elementul rădăcină este reprezentat de containerul din care acestea fac parte. | Fiecare element din cadrul unei scene este exprimat sub forma unui nod dintr-un graf (arbore) în care elementul rădăcină este reprezentat de containerul din care acestea fac parte. | ||
| Line 209: | Line 209: | ||
| * ''Node'': clasă abstractă pentru toate elementele grafice, folosită în special pentru nodurile care nu pot avea copii (nod frunză); clasele ce pot fi folosite în acest scop sunt definite mai cu seamă în pachetele ''javafx.scene.shape'' şi ''javafx.scene.text''. | * ''Node'': clasă abstractă pentru toate elementele grafice, folosită în special pentru nodurile care nu pot avea copii (nod frunză); clasele ce pot fi folosite în acest scop sunt definite mai cu seamă în pachetele ''javafx.scene.shape'' şi ''javafx.scene.text''. | ||
| - | {{ :laboratoare:laborator03:figura03.png?nolink&500 }} | + | {{ :laboratoare:laborator09:03scenegraph.png?nolink&500 }} |
| Aceste clase de bază definesc funcţionalităţi importante care vor fi moştenite de subclasele lor, inclusiv ordinea de desenare, vizibilitatea, compunerea transformărilor sau suportul pentru stiluri CSS. | Aceste clase de bază definesc funcţionalităţi importante care vor fi moştenite de subclasele lor, inclusiv ordinea de desenare, vizibilitatea, compunerea transformărilor sau suportul pentru stiluri CSS. | ||
| Line 290: | Line 290: | ||
| Clasele care implementează controale din JavaFX se găsesc în pachetul ''javafx.scene.control''. Fiecare control dispune de atribute şi metode (suplimentare faţă de cele din clasa ''Control'') pentru a oferi suport complet în interacţiunea cu utilizatorul. Clasa ''Control'' poate fi extinsă definindu-se un obiect care corespunde nevoilor utilizatorilor. | Clasele care implementează controale din JavaFX se găsesc în pachetul ''javafx.scene.control''. Fiecare control dispune de atribute şi metode (suplimentare faţă de cele din clasa ''Control'') pentru a oferi suport complet în interacţiunea cu utilizatorul. Clasa ''Control'' poate fi extinsă definindu-se un obiect care corespunde nevoilor utilizatorilor. | ||
| - | {{ :laboratoare:laborator03:figura04.png?nolink&700 }} | + | {{ :laboratoare:laborator09:04javafxgraphicelements.png?nolink&700 }} |
| Pe lângă controalele clasice (existente în AWT şi Swing) au fost dezvoltate şi alte componente, precum ''Accordion'', ''ColorPicker'', ''Chart'', ''DatePicker'', ''Pagination'', ''TitledPane'', ''TreeTableView''. | Pe lângă controalele clasice (existente în AWT şi Swing) au fost dezvoltate şi alte componente, precum ''Accordion'', ''ColorPicker'', ''Chart'', ''DatePicker'', ''Pagination'', ''TitledPane'', ''TreeTableView''. | ||
| Line 360: | Line 360: | ||
| ^ ^ ''INDETERMINATE=false'' ^ ''INDETERMINATE=true'' ^ | ^ ^ ''INDETERMINATE=false'' ^ ''INDETERMINATE=true'' ^ | ||
| - | | **''SELECTED=false''** | {{ :laboratoare:laborator03:checkbox-state1.png?nolink }} | {{ :laboratoare:laborator03:checkbox-state2.png?nolink }} | | + | | **''SELECTED=false''** | {{ :laboratoare:laborator09:05checkbox-state1.png?nolink }} | {{ :laboratoare:laborator09:05checkbox-state2.png?nolink }} | |
| - | | **''SELECTED=true''** | {{ :laboratoare:laborator03:checkbox-state3.png?nolink }} | ::: | | + | | **''SELECTED=true''** | {{ :laboratoare:laborator09:05checkbox-state3.png?nolink }} | ::: | |
| === ChoiceBox === | === ChoiceBox === | ||
| Line 642: | Line 642: | ||
| <code java> | <code java> | ||
| Hyperlink hyperlink = new Hyperlink(); | Hyperlink hyperlink = new Hyperlink(); | ||
| - | String URL = "http://aipi2014.andreirosucojocaru.ro"; | + | String URL = "http://aipi2015.andreirosucojocaru.ro"; |
| hyperlink.setText(URL); | hyperlink.setText(URL); | ||
| hiperlink.setOnAction((ActionEvent actionEvent) -> { | hiperlink.setOnAction((ActionEvent actionEvent) -> { | ||
| Line 1055: | Line 1055: | ||
| </code> | </code> | ||
| - | {{ :laboratoare:laborator03:menus.png?nolink }} | + | {{ :laboratoare:laborator09:06menus.png?nolink }} |
| ==== Diagrame ==== | ==== Diagrame ==== | ||
| Line 1098: | Line 1099: | ||
| LineChart<String, Number> lineChart = new LineChar<String, Number>(xAxis, yAxis); | LineChart<String, Number> lineChart = new LineChar<String, Number>(xAxis, yAxis); | ||
| lineChart.setTitle("Profit Statistics"); | lineChart.setTitle("Profit Statistics"); | ||
| - | XYChart.Series series2014 = new XYChart.Series(); | + | XYChart.Series series2015 = new XYChart.Series(); |
| - | series2014.setName("Year 2014"); | + | series2015.setName("Year 2015"); |
| - | series2014.getData().add(new XYChart.Data("January", ...)); | + | series2015.getData().add(new XYChart.Data("January", ...)); |
| // ... | // ... | ||
| - | lineChart.getData.add(series2014); | + | lineChart.getData.add(series2015); |
| </code> | </code> | ||
| Line 1218: | Line 1219: | ||
| <code java> | <code java> | ||
| - | String URL = "http://aipi2014.andreirosucojocaru.ro"; | + | String URL = "http://aipi2015.andreirosucojocaru.ro"; |
| WebView webView = new WebView(); | WebView webView = new WebView(); | ||
| WebEngine webEngine = webView.getEngine(); | WebEngine webEngine = webView.getEngine(); | ||
| Line 1448: | Line 1449: | ||
| Un **tip de eveniment** este o instanţă a clasei ''EventType'', acestea fiind organizate ierarhic, pe nivelul cel mai înalt aflându-se ''Event.ROOT'' echivalent cu ''Event.ANY''. Pe fiecare nivel, subtipul ''ANY'' este folosit pentru a desemna orice tip de eveniment din clasă. Mecanismul este foarte util în cazul în care se folosesc filtre pentru evenimente, putându-se trata fie doar anumite tipuri de evenimente, fie toate tipurile de evenimente specifice clasei respective. | Un **tip de eveniment** este o instanţă a clasei ''EventType'', acestea fiind organizate ierarhic, pe nivelul cel mai înalt aflându-se ''Event.ROOT'' echivalent cu ''Event.ANY''. Pe fiecare nivel, subtipul ''ANY'' este folosit pentru a desemna orice tip de eveniment din clasă. Mecanismul este foarte util în cazul în care se folosesc filtre pentru evenimente, putându-se trata fie doar anumite tipuri de evenimente, fie toate tipurile de evenimente specifice clasei respective. | ||
| - | {{ :laboratoare:laborator03:event_type_hierarchy.png?nolink }} | + | {{ :laboratoare:laborator09:07event_type_hierarchy.png?nolink }} |
| **Sursa** unui eveniment desemnează nodul care are asociat mecanismul de tratare a evenimentului, aflându-se cel mai aproape de nodul propriu-zis (asupra căruia s-a exercitat acţiunea respectivă) în ierarhia de obiecte a scenei. | **Sursa** unui eveniment desemnează nodul care are asociat mecanismul de tratare a evenimentului, aflându-se cel mai aproape de nodul propriu-zis (asupra căruia s-a exercitat acţiunea respectivă) în ierarhia de obiecte a scenei. | ||
| Line 1471: | Line 1472: | ||
| După ce evenimentul a ajuns la nodul ţintă şi / sau a fost procesat de către toate filtrele, acesta se va //întoarce la nodul rădăcină// (//eng.// event bubbling phase), urmărind parcursul dat de lanţul de propagare, însă în sens invers. Dacă vreun nod în lanţul de propagare are definită o metodă de tratare pentru respectivul tip de eveniment, aceasta este apelată, transmiţând evenimentul mai departe după ce se termină. Evenimentul va fi transmis într-un final şi nodului rădăcină doar dacă metodele de tratare a acestuia nu îl consumă. | După ce evenimentul a ajuns la nodul ţintă şi / sau a fost procesat de către toate filtrele, acesta se va //întoarce la nodul rădăcină// (//eng.// event bubbling phase), urmărind parcursul dat de lanţul de propagare, însă în sens invers. Dacă vreun nod în lanţul de propagare are definită o metodă de tratare pentru respectivul tip de eveniment, aceasta este apelată, transmiţând evenimentul mai departe după ce se termină. Evenimentul va fi transmis într-un final şi nodului rădăcină doar dacă metodele de tratare a acestuia nu îl consumă. | ||
| - | {{ :laboratoare:laborator03:event_managing.png?nolink&600 }} | + | {{ :laboratoare:laborator09:08event_managing.png?nolink&600 }} |
| În JavaFX, gestiunea evenimentelor se face prin **filtre** şi **metode de tratare**, acestea fiind implementări ale interfeţei ''EventHandler''. Atunci când se dorește ca aplicaţia să fie notificată de apariţia unui eveniment, trebuie asociat fie un filtru, fie o metodă de tratare specifică evenimentului respectiv. | În JavaFX, gestiunea evenimentelor se face prin **filtre** şi **metode de tratare**, acestea fiind implementări ale interfeţei ''EventHandler''. Atunci când se dorește ca aplicaţia să fie notificată de apariţia unui eveniment, trebuie asociat fie un filtru, fie o metodă de tratare specifică evenimentului respectiv. | ||
| Line 1546: | Line 1547: | ||
| </code> | </code> | ||
| - | {{ :laboratoare:laborator03:observer_listener.png?nolink&600 }} | + | {{ :laboratoare:laborator09:09observer_listener.png?nolink&600 }} |
| Un filtru de eveniment permite gestiunea evenimentului în timpul etapei de capturare propriu-zisă. Un nod poate avea asociate unul sau mai multe filtre pentru gestiunea unui eveniment. De asemenea un filtru poate fi utilizat pentru unul sau mai multe noduri, pentru unul sau mai multe tipuri de evenimente. Totodată, nodurile părinte pot oferi o procesare de nivel înalt a evenimentului pentru nodurile fiu, interceptându-le astfel ca acestea să nu mai ajungă la ţintă. Pentru a gestiona un eveniment în timpul etapei de capturare propriu-zisă, nodul trebuie sa asocieze un filtru de eveniment, care implementează interfaţa ''EventHandler''. Metoda ''handle()'' conţine codul executat la momentul în care evenimentul ajunge la nodul care are filtrul asociat. | Un filtru de eveniment permite gestiunea evenimentului în timpul etapei de capturare propriu-zisă. Un nod poate avea asociate unul sau mai multe filtre pentru gestiunea unui eveniment. De asemenea un filtru poate fi utilizat pentru unul sau mai multe noduri, pentru unul sau mai multe tipuri de evenimente. Totodată, nodurile părinte pot oferi o procesare de nivel înalt a evenimentului pentru nodurile fiu, interceptându-le astfel ca acestea să nu mai ajungă la ţintă. Pentru a gestiona un eveniment în timpul etapei de capturare propriu-zisă, nodul trebuie sa asocieze un filtru de eveniment, care implementează interfaţa ''EventHandler''. Metoda ''handle()'' conţine codul executat la momentul în care evenimentul ajunge la nodul care are filtrul asociat. | ||
| Line 1952: | Line 1953: | ||
| Pe baza acestei ierarhii de nodurilor din fișierul FXML, se poate construi graful de scene: | Pe baza acestei ierarhii de nodurilor din fișierul FXML, se poate construi graful de scene: | ||
| - | {{ :laboratoare:laborator03:authentication.png?nolink&800 }} | + | {{ :laboratoare:laborator09:10authentication.png?nolink&800 }} |
| De remarcat faptul că elementul rădăcină (de tip ''AnchorPane'') specifică proprietatea ''fx:controller'', indicând clasa care se va ocupa de gestiunea fișierului FXML, aceasta fiind ''ro.pub.cs.aipi.lab09.controller.Authentication''. | De remarcat faptul că elementul rădăcină (de tip ''AnchorPane'') specifică proprietatea ''fx:controller'', indicând clasa care se va ocupa de gestiunea fișierului FXML, aceasta fiind ''ro.pub.cs.aipi.lab09.controller.Authentication''. | ||
| Line 1989: | Line 1990: | ||
| } | } | ||
| </code> | </code> | ||
| - | ==== JavaFX Scene Builder 2.0 ==== | ||
| - | Documentele FXML pot fi dezvoltate în mod vizual folosind utilitarul [[http://www.oracle.com/technetwork/java/javase/downloads/sb2download-2177776.html|SceneBuilder 2.0]], disponibil pe platformele Windows (32/64 de biţi), Mac OS X şi Linux (32/64 biţi) – sub formă de pachete ''.deb'' şi arhive ''tar.gz''. | + | ==== JavaFX Scene Builder ==== |
| + | |||
| + | Documentele FXML pot fi dezvoltate în mod vizual folosind utilitarul [[http://gluonhq.com/open-source/scene-builder/|SceneBuilder]], disponibil pe platformele Windows (32/64 de biţi), Mac OS X şi Linux (32/64 biţi) – sub formă de pachete ''.deb'' şi arhive ''tar.gz''. | ||
| Acesta permite construirea interfeței grafice interactiv, simultan cu generarea automată a documentului FXML, fiecare control putând fi configurat, grupate în proprietăţi, moduri de dispunere şi cod. | Acesta permite construirea interfeței grafice interactiv, simultan cu generarea automată a documentului FXML, fiecare control putând fi configurat, grupate în proprietăţi, moduri de dispunere şi cod. | ||
| Line 2009: | Line 2011: | ||
| * gratuitate pentru toate tipurile de utilizatori. | * gratuitate pentru toate tipurile de utilizatori. | ||
| - | {{ :laboratoare:laborator03:scene_builder.png?nolink&800 }} | + | {{ :laboratoare:laborator09:11scene_builder.png?nolink&800 }} |
| - | Utilitarul Scene Builder 2.0 are interfața grafică împărțită în 7 secțiuni. | + | Utilitarul Scene Builder are interfața grafică împărțită în 7 secțiuni. |
| În secţiunea 1 (''Library'') este disponibilă o bibliotecă de componente cu care se poate construi interfaţa cu utilizatorul. Ele pot indica modul de dispunere al obiectelor sau pot constitui controale propriu-zise. Acestea sunt grupate pe categorii: ''Containers'', ''Controls'', ''Menu'', ''Miscellaneous'', ''Shapes'', ''Charts'', ''3D''. | În secţiunea 1 (''Library'') este disponibilă o bibliotecă de componente cu care se poate construi interfaţa cu utilizatorul. Ele pot indica modul de dispunere al obiectelor sau pot constitui controale propriu-zise. Acestea sunt grupate pe categorii: ''Containers'', ''Controls'', ''Menu'', ''Miscellaneous'', ''Shapes'', ''Charts'', ''3D''. | ||
| Line 2175: | Line 2177: | ||
| În privinţa nivelului de logică a aplicaţiei, cele mai frecvent uzitate tehnici sunt: | În privinţa nivelului de logică a aplicaţiei, cele mai frecvent uzitate tehnici sunt: | ||
| - | * folosirea ecrane animatelor care să indice progresul acelor procese de lungă durată astfel încât aplicaţia să nu pară că nu răspunde la interacţiunea cu utilizatorul; | + | * folosirea ecranelor animate care să indice progresul acelor procese de lungă durată astfel încât aplicaţia să nu pară că nu răspunde la interacţiunea cu utilizatorul; |
| * folosirea unor denumiri simbolice pentru pachetele implementate pentru ca aplicaţia să fie mai organizată şi mai uşor de întreţinut; | * folosirea unor denumiri simbolice pentru pachetele implementate pentru ca aplicaţia să fie mai organizată şi mai uşor de întreţinut; | ||
| * implementarea arhitecturii model-view-controller (MVC) prin intermediul fişierelor FXML pentru definirea scenelor, acestea reprezentând partea de vizualizare, în timp ce modelul este reprezentat de obiectele de domeniu specifice aplicaţiei, iar partea de control aparţine codului Java care defineşte comportamentul interfeţei grafice în interacţiunea cu utilizatorul; | * implementarea arhitecturii model-view-controller (MVC) prin intermediul fişierelor FXML pentru definirea scenelor, acestea reprezentând partea de vizualizare, în timp ce modelul este reprezentat de obiectele de domeniu specifice aplicaţiei, iar partea de control aparţine codului Java care defineşte comportamentul interfeţei grafice în interacţiunea cu utilizatorul; | ||
| Line 2190: | Line 2192: | ||
| ===== Activitate de Laborator ===== | ===== Activitate de Laborator ===== | ||
| - | Se doreşte implementarea unei interfeţe grafice cu utilizatorul pentru gestiunea informaţiilor dintr-o bază de date, aceasta urmând a fi utilizată pentru un sistem ERP destinat unei librării care comercializează doar cărţi. | + | Se doreşte implementarea unei interfeţe grafice cu utilizatorul pentru gestiunea informaţiilor dintr-o bază de date, aceasta urmând a fi utilizată pentru un sistem ERP destinat unei librării care comercializează doar cărţi. |
| Aplicaţia va avea două ferestre: | Aplicaţia va avea două ferestre: | ||
| Line 2196: | Line 2198: | ||
| * o fereastră în care sunt implementate operaţiile pentru o tabelă dintr-o bază de date (adăugare, modificare, ştergere, căutare). | * o fereastră în care sunt implementate operaţiile pentru o tabelă dintr-o bază de date (adăugare, modificare, ştergere, căutare). | ||
| - | - Să se ruleze scriptul Laborator09l.sql în ''MySQL Workbench''. În interfata ''Constants'' din pachetul ''ro.pub.cs.aipi.lab09.general'', să se modifice corespunzător proprietăţile ''DATABASE_USERNAME'', respectiv ''DATABASE_PASSWORD''.\\ \\ Structura formularului pentru autentificarea utilizatorilor în sistem este descrisă într-un document FXML (''authentication.fxml'') generat cu SceneBuilder 2.0.\\ {{ :laboratoare:laborator03:activitate_de_laborator01.png?nolink }}\\ | + | **0**. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://www.github.com/aipi2015/Laborator09]]. În urma acestei operații, directorul Laborator09 va trebui să conțină subdirectorul ''labtasks'', fișierele ''README.md'' și ''LICENSE''. |
| - | - Să se deschidă fişierul ''authentication.fxml'' cu SceneBuilder 2.0 sau cu orice alt editor de text – inclusiv în cadrul NetBeans / Eclipse. | + | |
| - | - Să se precizeze clasa controller pentru documentul FXML (având ca rădăcină un mecanism de gestiune a conținutului de tip ''AnchorPane'') ca fiind ''ro.pub.cs.aipi.lab09.controller.Authentication''. | + | <code> |
| - | - Pentru butoanele ''Ok'' / ''Cancel'', să se specifice metodele care vor fi executate în cazul producerii unui eveniment de tipul ''ActionEvent.ACTION'' (proprietatea ''onAction()'') ca fiind: | + | student@aipi2015:~$ git clone https://www.github.com/aipi2015/Laborator09.git |
| - | * ''okButtonHandler()''; | + | </code> |
| - | * ''cancelButtonHandler()''; | + | |
| - | - Să se specifice identificatori (proprietatea ''fx:id'') pentru: | + | **1**. **a)** Să se ruleze, folosind MySQL Workbench (sau alt utilitar similar), scriptul ''Laborator09l.sql'', localizat în directorul ''scripts'' din cadrul proiectului ''09-BookStore-JavaFX''. Acesta instalează baza de date ''bookstore''. |
| - | * cele două câmpuri text pentru specificarea parametrilor nume utilizator / parolă, având denumirile ''usernameTextField'' / ''passwordTextField''; | + | |
| - | * eticheta în care se va afișa un mesaj de eroare în situația în care operația de autentificare eșuează, având denumirea ''errorLabel''. | + | <note>În cazul în care baza de date ''bookstore'' este deja instalată, nu mai este necesar să se ruleze acest script.</note> |
| - | - În clasa controller ''ro.pub.cs.aipi.lab09.controller.Authentication'', sa se implementeze metodele ''okButtonHandler()'' / ''cancelButtonHandler()'' ce gestionează accesul utilizatorului în aplicaţie. Vor avea dreptul de a accesa sistemul informatic doar utilizatorii al căror rol este administrator. În cazul în care combinaţia (utilizator, parolă) este incorectă sau utilizatorul nu are acces în sistem, se va afişa un mesaj corespunzător care va rămâne pe ecran maxim 5 secunde. | + | |
| - | - În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'' să se populeze obiectul ''attributesGridPane'' care va avea pe două coloane obiectele de tip ''ArrayList<Label>'' (''attributeLabels''), respectiv ''ArrayList<Control>'' (''attributeControls'').\\ Înainte de a adăuga obiectul propriu-zis, trebuie să îi specificaţi poziţia în cadrul obiectului de tip ''GridPane'', prin intermediul metodei ''GridPane.setConstraints(object, column ,row)''.\\ \\ În interfaţa grafică se folosesc cinci butoane (adăugare, editare, ştergere, căutare, golire conținut) pentru a se realiza operaţiile de tip CRUD.\\ În momentul când se produce o acţiune corespunzătoare unuia dintre aceste acestea, se vor apela metodele corespunzătoare evenimentului de tip ''onAction()'', specificate în fișierul FXML.\\ {{ :laboratoare:laborator03:activitate_de_laborator02.png?nolink }}\\ | + | <note important>Această operație poate dura o perioadă de timp îndelungată, în funcție de configurațiile mașinilor pe care se lucrează.</note> |
| - | - În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'', să se implementeze metoda ''updateButtonHandler()'' corespunzătoare butonului care actualizează valorile din controale (câmpuri text și liste de selecție) în tabelă. | + | |
| - | - În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'', să se implementeze metoda ''clearButtonHandler()'' care curăță conținutul câmpurilor text și al listelor de selecție, lăsând completată numai valoarea corespunzătoare cheii primare autoincrementale cu următoarea valoare din baza de date. | + | **b)** În interfața ''Constants'' din pachetul ''ro.pub.cs.aipi.lab09.general'', să se modifice credențialele (nume de utilizator și parolă), astfel încât să se poată accesa informațiile din baza de date MySQL (proprietățile ''DATABASE_USERNAME'' respectiv ''DATABASE_PASSWORD''). |
| - | - Să se creeze un submeniu ''About'' al meniului ''Help'' care în momentul când este accesat deschide o fereastră în care sunt afişate pictograma (fișierul ''icon.png'' din directorul ''resources/images'') și denumirea aplicaţiei, versiunea şi anul în care a fost realizată. Totodată, aceasta va conţine un buton care va închide fereastra atunci când este apăsat.\\ Se poate folosi clasa ''Dialog'' în acest sens. | + | |
| - | - Să se creeze un buton de căutare la apăsarea căruia vor fi afişate în tabel toate înregistrările care corespund unor anumite criterii (au anumite valori corespunzătoare unor anumite atribute, specificate de utilizator în câmpurile text şi listele derulante, ignorând cheia primară). | + | **2**. Să se acceseze fişierul ''authentication.fxml'' (localizat în directorul ''src'', subdirectorul ''resources/fxml'') folosind utilitarul SceneBuilder sau cu orice alt editor de text – inclusiv din cadrul mediilor integrate de dezvoltare Eclipse / Eclipse. Acesta conține un formular pentru autentificarea utilizatorilor. |
| - | - Să se creeze o animaţie asupra butonului deasupra căruia se găseşte mouse-ul astfel încât acesta să aibă un efect de tip umbră exterioară (''DropShadow'') implementând şi o tranziţie care pe parcursul unei secunde modifică transparenţa de la 1.0 la 0.8 şi scalează dimensiunile cu 0.1. Animaţia trebuie cât timp mouse-ul se găseşte deasupra butonului având un caracter ciclic. Metoda se va implementa pentru toate butoanele ce realizează modificări asupra înregistrărilor din baza de date. | + | |
| + | {{ :laboratoare:laborator09:activitate_de_laborator01.png?nolink }} | ||
| + | ⇓ | ||
| + | {{ :laboratoare:laborator09:activitate_de_laborator02.png?nolink }} | ||
| + | |||
| + | **a)** Să se precizeze clasa controller pentru documentul FXML (având ca rădăcină un mecanism de gestiune a conținutului de tip ''AnchorPane'') ca fiind ''ro.pub.cs.aipi.lab09.controller.Authentication''. | ||
| + | |||
| + | {{ :laboratoare:laborator09:activitate_de_laborator03.png?nolink }} | ||
| + | |||
| + | <file xml authentication.xml> | ||
| + | <!-- different imports --> | ||
| + | |||
| + | <AnchorPane fx:id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" | ||
| + | prefHeight="128.0" prefWidth="336.0000999999975" | ||
| + | style="-fx-background-color: beige;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ro.pub.cs.aipi.lab09.controller.Authentication"> | ||
| + | |||
| + | <!-- description of the graphic user interface --> | ||
| + | |||
| + | </AnchorPane> | ||
| + | </file> | ||
| + | |||
| + | **b)** Pentru butoanele ''Ok'' / ''Cancel'', să se specifice metodele care vor fi executate în cazul producerii unui eveniment de tipul ''ActionEvent.ACTION'' (proprietatea ''onAction()'') ca fiind: | ||
| + | * ''okButtonHandler()''; | ||
| + | * ''cancelButtonHandler()''. | ||
| + | |||
| + | {{ :laboratoare:laborator09:activitate_de_laborator04a.png?nolink }} ⇒ {{ :laboratoare:laborator09:activitate_de_laborator04b.png?nolink }} | ||
| + | |||
| + | <file xml authentication.xml> | ||
| + | <!-- ... --> | ||
| + | <Button mnemonicParsing="false" onAction="#okButtonHandler" text="OK" /> | ||
| + | <!-- ... --> | ||
| + | </file> | ||
| + | |||
| + | **c)** Să se specifice identificatori (proprietatea ''fx:id'') pentru: | ||
| + | * cele două câmpuri text pentru specificarea parametrilor nume utilizator / parolă, având denumirile ''usernameTextField'' / ''passwordTextField''; | ||
| + | * eticheta în care se va afișa un mesaj de eroare în situația în care operația de autentificare eșuează, având denumirea ''errorLabel''. | ||
| + | |||
| + | {{ :laboratoare:laborator09:activitate_de_laborator05a.png?nolink }} ⇒ {{ :laboratoare:laborator09:activitate_de_laborator05b.png?nolink }} | ||
| + | |||
| + | <file xml authentication.xml> | ||
| + | <!-- ... --> | ||
| + | <TextField fx:id="usernameTextField" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="0" /> | ||
| + | <!-- ... --> | ||
| + | </file> | ||
| + | |||
| + | **d)** În clasa controller ''ro.pub.cs.aipi.lab09.controller.Authentication'', sa se implementeze metodele ''okButtonHandler()'' / ''cancelButtonHandler()'' ce gestionează accesul utilizatorului în aplicaţie. Vor avea dreptul de a accesa sistemul informatic doar utilizatorii al căror tip este administrator. În cazul în care combinaţia (utilizator, parolă) este incorectă sau utilizatorul nu are acces în sistem, se va afişa un mesaj corespunzător care va rămâne pe ecran maxim 5 secunde. | ||
| + | |||
| + | <spoiler|Indicații de rezolvare> | ||
| + | În cazul butonului ''Ok'', este necesar să se realizeze o interogare asupra tabelei ''user'' (accesibil prin proprietatea ''Constants.USER_TABLE_NAME''). Aceasta va prelua atributul ''type'' (disponibil în câmpul ''Constants.USER_TYPE_ATRRIBUTE'') pentru acele înregistrări care au câmpurile ''username'' și ''password'' egale cu valorile introduse prin intermediul interfeței grafice. | ||
| + | |||
| + | <code java> | ||
| + | DatabaseOperations databaseOperations = DatabaseOperationsImplementation.getInstance(); | ||
| + | List<String> attributes = new ArrayList<>(); | ||
| + | attributes.add(Constants.USER_TYPE_ATTRIBUTE); | ||
| + | List<List<String>> userTypes = databaseOperations.getTableContent( | ||
| + | Constants.USER_TABLE_NAME, | ||
| + | attributes, | ||
| + | "username=\'" + usernameTextField.getText() + "\' AND password=\'" + passwordTextField.getText() + "\'", | ||
| + | null, | ||
| + | null, | ||
| + | null); | ||
| + | </code> | ||
| + | |||
| + | În urma execuției acestei interogări, se pot distinge următoarele situații: | ||
| + | - interogarea nu întoarce nici o valoare, ceea ce înseamnă că perechea (nume de utilizator, parolă) este incorectă: în această situație se va afișa un mesaj de eroare corespunzător, conținut de proprietatea ''Constants.ERROR_USERNAME_PASSWORD''; acesta va fi afișat timp de 5 secunde, iar conținutul câmpurilor nume de utilizator și parolă vor fi golite; | ||
| + | - interogarea întoarce valori, situație în care se verifică tipul utilizatorului: | ||
| + | - utilizatorul are tipul ''administrator'' (reținut de ''Constants.USER_TYPE_ADMINISTRATOR_VALUE''); în această situație se deschide interfața grafică reprezentată de clasa ''Container'' și se închide interfața grafică reprezentată de clasa (''Authentication'') - aceasta trebuie accesată prin intermediul grafului de scene atașat evenimentului, întrucât procesarea se face prin intermediul firului de execuție JavaFX, responsabil cu gestiunea operațiilor cu utilizatorul: <code java> | ||
| + | new Container().start(); | ||
| + | ((Node) event.getSource()).getScene().getWindow().hide(); | ||
| + | </code> | ||
| + | - utilizatorul are tipul ''client'' (reținut de ''Constants.USER_TYPE_CLIENT_VALUE''): în această situație se va afișa un mesaj de eroare corespunzător, conținut de proprietatea ''Constants.ERROR_ACCESS_NOT_ALLOWED''; acesta va fi afișat timp de 5 secunde, iar conținutul câmpurilor nume de utilizator și parolă vor fi golite. | ||
| + | |||
| + | Afișarea unui mesaj de eroare pentru care se atașează o animație de tipul dispariție graduală, pe parcursul unui interval de timp este realizată prin intermediul unui obiect de tip [[https://docs.oracle.com/javafx/2/api/javafx/animation/FadeTransition.html|FadeTransition]]: <code java> | ||
| + | errorLabel.setText(...); | ||
| + | FadeTransition fadeTransition = new FadeTransition(Duration.seconds(5), errorLabel); | ||
| + | fadeTransition.setFromValue(1.0); | ||
| + | fadeTransition.setToValue(0.0); | ||
| + | fadeTransition.play(); | ||
| + | usernameTextField.setText(""); | ||
| + | passwordTextField.setText(""); | ||
| + | </code> | ||
| + | |||
| + | În cazul butonului ''Cancel'', se va apela metoda ''Platform.exit()'' | ||
| + | </spoiler> | ||
| + | |||
| + | **3**. În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'' să se completeze metoda ''initialize()'', astfel încât aceasta să populeze obiectul ''attributesGridPane'' (de tip ''GridPane'') care va avea pe două coloane obiectele de tip ''ArrayList<Label>'' (''attributeLabels''), respectiv ''ArrayList<Control>'' (''attributeControls''). | ||
| + | |||
| + | {{ :laboratoare:laborator09:activitate_de_laborator06.png?nolink }} | ||
| + | |||
| + | <note important>Obictele ''attributeLabels'', respectiv ''attributesControls'' au deja conținutul care trebuie vizualizat în cadrul acestora.</note> | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | Înainte de a adăuga obiectul propriu-zis, trebuie să îi specificaţi poziţia în cadrul obiectului de tip ''GridPane'', prin intermediul metodei [[https://docs.oracle.com/javafx/2/api/javafx/scene/layout/GridPane.html#setConstraints%28javafx.scene.Node,%20int,%20int%29|GridPane.setConstraints(Node, column ,row)]]. | ||
| + | |||
| + | Atașarea propriu-zisă a nodului se face prin intermediul metodei ''add()'' apelată pe lista observabilă a nodurilor (copil) a obiectului ''attributesGridPane'', obținută prin invocarea metodei [[https://docs.oracle.com/javafx/2/api/javafx/scene/layout/Pane.html#getChildren%28%29|getChildren()]]. | ||
| + | |||
| + | <code java> | ||
| + | GridPane.setConstraints(attributesLabels.get(attributesLabels.size() - 1), 0, currentIndex); | ||
| + | attributesGridPane.getChildren().add(attributesLabels.get(attributesLabels.size() - 1)); | ||
| + | |||
| + | // ... | ||
| + | |||
| + | GridPane.setConstraints(attributesControls.get(attributesControls.size() - 1), 1, currentIndex); | ||
| + | attributesGridPane.getChildren().add(attributesControls.get(attributesControls.size() - 1)); | ||
| + | </code> | ||
| + | |||
| + | Toate aceste operații vor fi realizate repetitiv, pe o iterație care parcurge toate atributele din cadrul entității respective. | ||
| + | |||
| + | </spoiler> | ||
| + | |||
| + | Așa cum se poate observa, în interfaţa grafică se folosesc cinci butoane (adăugare, editare, ştergere, căutare, golire conținut) pentru a se realiza operaţiile de tip CRUD (Create, Read, Update, Delete).\\ În momentul când se produce o acţiune corespunzătoare unuia dintre aceste acestea, se vor apela metodele corespunzătoare evenimentului de tip ''onAction()'', specificate în fișierul FXML. | ||
| + | |||
| + | **4**. În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'', să se implementeze metoda ''updateButtonHandler()'' corespunzătoare butonului care actualizează valorile din controale (câmpuri text și liste de selecție) în tabelă. | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | În cadrul metodei ''updateButttonHandler()'' trebuie realizate următoarele operații: | ||
| + | - se obține indexul selectat în mod curent în cadrul tabelului (astfel încât selecția să poată fi refăcută ulterior operației de actualizare) <code java> | ||
| + | int tableViewSelectedIndex = tableContentTableView.getSelectionModel().getSelectedIndex(); | ||
| + | </code> | ||
| + | - se obține o listă cu toate valorile selectate în mod curent (astfel încât operația de actualizare să fie realizată pe baza identificatorului care are rol de cheie primară în tabelă) <code java> | ||
| + | List<String> tableViewCurrentRecord = ((Model) tableContentTableView.getSelectionModel().getSelectedItem()).getValues(); | ||
| + | </code> | ||
| + | - se parcurge lista de etichete / controale, astfel încât să se construiască: | ||
| + | - lista de atribute, pe baza listei de etichete <code java> | ||
| + | attributes.add(attributesLabels.get(currentIndex).getText()); | ||
| + | </code> | ||
| + | - lista de valori: se obține conținutul listei derulante și pe baza ei se determină identificatorul înregistrării din tabela referită, prin intermediul metodei ''getForeignKeyValue()'' care primește ca argumente denumirea tabelei, denumirea atributului precum și lista cu valori <code java> | ||
| + | if (attributeControl instanceof ComboBox) { | ||
| + | try { | ||
| + | String parentTableValues = ((ComboBox<String>) attributeControl).getValue(); | ||
| + | if (parentTableValues != null && !parentTableValues.isEmpty()) { | ||
| + | value = databaseOperations.getForeignKeyValue(tableName, attributesLabels.get(currentIndex).getText(), Utilities.decompress(parentTableValues)); | ||
| + | } | ||
| + | } catch (Exception sqlException) { | ||
| + | System.out.println("An exception had occured: " + sqlException.getMessage()); | ||
| + | if (Constants.DEBUG) { | ||
| + | sqlException.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | } else { | ||
| + | value = ((TextField) attributeControl).getText(); | ||
| + | } | ||
| + | if (value != null && !value.isEmpty()) { | ||
| + | values.add(value); | ||
| + | } | ||
| + | </code> | ||
| + | - actualizarea propriu-zisă este realizată prin intermediul metodei ''updateRecordsIntoTable()'' care primește ca parametrii denumirea tabelei, lista de atribute, lista de valori precum și condiția pentru înregistrarea care este actualizată (pe baza valorii cheii primare, determinată anterior) <code java> | ||
| + | try { | ||
| + | if (attributes != null && values != null && !attributes.isEmpty() && !values.isEmpty()) { | ||
| + | databaseOperations.updateRecordsIntoTable(tableName, | ||
| + | attributes, | ||
| + | values, | ||
| + | databaseOperations.getTablePrimaryKey(tableName) + "=\'" + tableViewCurrentRecord.get(0) + "\'"); | ||
| + | } | ||
| + | } catch (SQLException | DatabaseException exception) { | ||
| + | System.out.println("An exception had occured: " + exception.getMessage()); | ||
| + | if (Constants.DEBUG) { | ||
| + | exception.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | - popularea tabelei cu noul conținut implică: | ||
| + | - apelarea metodei ''populate'', fără nici un criteriu de selecție; | ||
| + | - selectarea valorii care a fost actualizată: ''tableContentTableView.getSelectionModel().select(tableViewSelectedIndex);''; | ||
| + | - valorile corespunzătoare selecției curente nu mai sunt vizualizate în cadrul listei de controale; în acest scop se poate invoca metoda de tratare a evenimentului de apăsare a butonului //Clear//, fără nici un argument ca eveniment propriu-zis: <code java> | ||
| + | clearButtonHandler(null); | ||
| + | </code> | ||
| + | </spoiler> | ||
| + | |||
| + | **5**. În clasa ''TableManagement'', din pachetul ''ro.pub.cs.aipi.lab09.controller'', să se implementeze metoda ''clearButtonHandler()'' care curăță conținutul câmpurilor text și al listelor de selecție, lăsând completată numai valoarea corespunzătoare cheii primare autoincrementale cu următoarea valoare din baza de date. | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | Se parcurge, în cadrul unei iterații, lista de controale, acestea putând avea tipul: | ||
| + | - ''ComboBox'': se folosește metoda ''setValue()'' primind ca argument șirul vid: ''%%((ComboBox<String>) attributeControl).setValue("");%%''; | ||
| + | - ''TextField'': se utilizează metoda ''setText()'' primind ca argument: | ||
| + | - valoarea maximă a cheii primare în cadrul tabelei (pentru indexul 0, corespunzător identificatorului), prin intermediul metodei ''getTablePrimaryKeyMaximimValue()'', disponibilă în interfața ''DatabaseOperations'': ''%%((TextField) attributeControl).setText(Integer.toString(databaseOperations.getTablePrimaryKeyMaximumValue(tableName) + 1));%%''; | ||
| + | - șirul vid, altfel: ''%%((TextField) attributeControl).setText("");%%''. | ||
| + | </spoiler> | ||
| + | |||
| + | **6**. Să se creeze un submeniu ''About'' al meniului ''Help'' care în momentul când este accesat deschide o fereastră în care sunt afişate pictograma (fișierul ''icon.png'' din directorul ''resources/images'') și denumirea aplicaţiei, versiunea şi anul în care a fost realizată. Totodată, aceasta va conţine un buton care va închide fereastra atunci când este apăsat.\\ Se poate folosi clasa ''Dialog'' în acest sens. | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | În clasa ''Container'' din pachetul ''ro.pub.cs.aipi.lab09.controller'' se instanțiază un obiect de tip ''Dialog'' pentru care se specifică următoarele proprietăți: | ||
| + | * titlul este ''Constants.ABOUT_WINDOW_TITLE''; | ||
| + | * locația de la care se va încărca pictograma este ''Constants.ABOUT_ICON_LOCATION''; | ||
| + | * mesajul care va fi vizualizat este ''Constants.ABOUT_MESSAGE_CONTENT''. | ||
| + | Fereastra trebuie afișată prin invocarea metodei ''start()''. | ||
| + | |||
| + | <code java> | ||
| + | Dialog dialog = new Dialog(); | ||
| + | dialog.setProperties(Constants.ABOUT_WINDOW_TITLE, Constants.ABOUT_ICON_LOCATION, Constants.ABOUT_MESSAGE_CONTENT); | ||
| + | dialog.start(); | ||
| + | </code> | ||
| + | </spoiler> | ||
| + | |||
| + | **7**. Să se creeze un buton de căutare la apăsarea căruia vor fi afişate în tabel toate înregistrările care corespund unor anumite criterii (au anumite valori corespunzătoare unor anumite atribute, specificate de utilizator în câmpurile text şi listele derulante, ignorând cheia primară). | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | În clasa ''TableManagement'' din pachetul ''ro.pub.cs.aipi.lab09.controller'', se va implementa metoda ''searchButtonHandler()'', în cadrul căreia va fi parcursă, în cadrul unei iterații, lista de controale, pentru a se obține perechea de atribute și de valori pe baza cărora va fi realizată căutarea. | ||
| + | - dacă controlul este de tip ''ComboBox'' se identifică valoarea din tabela referită corespunzătoare atributului, prin invocarea metodei ''getForeignKeyValue()'': <code java> | ||
| + | if (attributeControl instanceof ComboBox) { | ||
| + | try { | ||
| + | String parentTableValues = ((ComboBox<String>) attributeControl).getValue(); | ||
| + | if (parentTableValues != null && !parentTableValues.isEmpty()) { | ||
| + | value = databaseOperations.getForeignKeyValue(tableName, attributesLabels.get(currentIndex).getText(), Utilities.decompress(parentTableValues)); | ||
| + | } | ||
| + | } catch (Exception exception) { | ||
| + | System.out.println("An exception had occured: " + exception.getMessage()); | ||
| + | if (Constants.DEBUG) { | ||
| + | exception.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | - în situația în care controlul este de tip ''TextField'', se identifică valoarea prin intermediul metodei ''getText()'': ''%%value = ((TextField) attributeControl).getText();%%'' | ||
| + | |||
| + | Se construiește clauza ''WHERE'' a interogării pe baza căreia este realizată căutarea folosind șablonul ''atribut='%valoare%''', aceasta fiind transmisă ca parametru al metodei ''populateTableView()'': <code java> | ||
| + | for (Control attributeControl : attributesControls) { | ||
| + | // ... | ||
| + | if (value != null && !value.isEmpty() && currentIndex != 0) { | ||
| + | whereClause += ((whereClause.isEmpty()) ? "" : " AND ") + attributesLabels.get(currentIndex).getText() + " LIKE \'%" + Utilities.escape(value) + "%\'"; | ||
| + | } | ||
| + | currentIndex++; | ||
| + | // ... | ||
| + | } | ||
| + | populateTableView(!whereClause.isEmpty() ? whereClause : null); | ||
| + | </code> | ||
| + | </spoiler> | ||
| + | |||
| + | **8**. Să se creeze o animaţie asupra butonului deasupra căruia se găseşte mouse-ul astfel încât acesta să aibă un efect de tip umbră exterioară ([[https://docs.oracle.com/javafx/2/api/javafx/scene/effect/DropShadow.html|DropShadow]]) implementând şi o tranziţie care pe parcursul unei secunde modifică transparenţa de la 1.0 la 0.8 şi scalează dimensiunile cu 0.1. Animaţia trebuie cât timp mouse-ul se găseşte deasupra butonului având un caracter ciclic. Metoda se va implementa pentru toate butoanele ce realizează modificări asupra înregistrărilor din baza de date. | ||
| + | |||
| + | <spoiler|Indicații de Rezolvare> | ||
| + | Se creează următoarele efecte: | ||
| + | - o umbră care va fi aplicată butoanelor (un obiect de tip ''DropShadow''); | ||
| + | - o animație de tip dispariție având transparențele variabile între 1 și 0.8, cu durata de 1 secundă, care se va repeta nedefinit (un obiect de tip ''FadeTransition''); | ||
| + | - o animație de tip scalare, având valorile de 0.1 atât pe axa Ox cât și pe aza Oy, cu durata de 1 secundă, care va rula nedefinit (un obiect de tip ''ScaleTransition''); | ||
| + | - un efect paralel, care include animațiile de tip dispariție, respectiv scalare, care vor fi aplicate concomitent (un obiect de tip ''ParallelTransition''). | ||
| + | |||
| + | <code java> | ||
| + | final DropShadow dropShadow = new DropShadow(); | ||
| + | final FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1)); | ||
| + | fadeTransition.setFromValue(1.0f); | ||
| + | fadeTransition.setToValue(0.8f); | ||
| + | fadeTransition.setCycleCount(Timeline.INDEFINITE); | ||
| + | fadeTransition.setAutoReverse(true); | ||
| + | final ScaleTransition scaleTransition = new ScaleTransition(Duration.seconds(1)); | ||
| + | scaleTransition.setByX(0.1f); | ||
| + | scaleTransition.setByY(0.1f); | ||
| + | scaleTransition.setCycleCount(Timeline.INDEFINITE); | ||
| + | scaleTransition.setAutoReverse(true); | ||
| + | final ParallelTransition parallelTransition = new ParallelTransition(fadeTransition, scaleTransition); | ||
| + | </code> | ||
| + | |||
| + | Pentru fiecare buton în parte, se vor defini metodele ascultător pentru fiecare dintre cele două evenimente: | ||
| + | - ''MouseEvent.MOUSE_ENTERED'' - se sta stabilește efectul de tip umbră, se transmite nodul ca obiect al animațiilor de tipul ''Fade'' și ''Scale'', după care tranziția paralelă este începută , folosind metoda ''play()''; | ||
| + | - ''MouseEvent.MOUSE_EXITED'' - se pierd efectele anterioare (metoda ''setEfect()'' primește argumentul null), durata animației devine 0, animațiile pierd referința de pe nod, iar tranziția paralelă este oprită, folosind metoda ''stop()''. | ||
| + | |||
| + | <code java> | ||
| + | buttonList.stream().map((currentButton) -> { | ||
| + | currentButton.addEventHandler(MouseEvent.MOUSE_ENTERED, (MouseEvent event) -> { | ||
| + | currentButton.setEffect(dropShadow); | ||
| + | fadeTransition.setNode(currentButton); | ||
| + | scaleTransition.setNode(currentButton); | ||
| + | parallelTransition.play(); | ||
| + | }); | ||
| + | return currentButton; | ||
| + | }).forEach((currentButton) -> { | ||
| + | currentButton.addEventHandler(MouseEvent.MOUSE_EXITED, (MouseEvent event) -> { | ||
| + | currentButton.setEffect(null); | ||
| + | parallelTransition.jumpTo(Duration.ZERO); | ||
| + | fadeTransition.setNode(null); | ||
| + | scaleTransition.setNode(null); | ||
| + | parallelTransition.stop(); | ||
| + | parallelTransition.setNode(null); | ||
| + | }); | ||
| + | }); | ||
| + | </code> | ||
| + | </spoiler> | ||
| ===== Resurse ====== | ===== Resurse ====== | ||
| Line 2220: | Line 2499: | ||
| ===== Soluții ===== | ===== Soluții ===== | ||
| - | TODO | + | [[https://github.com/aipi2015/Laborator09/tree/master/solutions/eclipse/09-BookStore-JavaFX|Proiect Eclipse]]\\ |
| + | [[https://github.com/aipi2015/Laborator09/tree/master/solutions/netbeans/09-BookStore-JavaFX|Proiect NetBeans]] | ||
