Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:laborator09 [2015/12/17 22:16]
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 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 1099: 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 1219: 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 1993: Line 1993:
 ==== JavaFX Scene Builder ==== ==== JavaFX Scene Builder ====
  
-Documentele FXML pot fi dezvoltate în mod vizual folosind utilitarul [[http://www.oracle.com/technetwork/java/javase/​downloads/​sb2download-2177776.html|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''​. ​+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 2177: 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 2204: Line 2204:
 </​code>​ </​code>​
  
-**1**. 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''​.+**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''​.
  
 <​note>​În cazul în care baza de date ''​bookstore''​ este deja instalată, nu mai este necesar să se ruleze acest script.</​note>​ <​note>​În cazul în care baza de date ''​bookstore''​ este deja instalată, nu mai este necesar să se ruleze acest script.</​note>​
Line 2210: Line 2210:
 <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>​ <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>​
  
-**2**. Să se modifice credențialele (nume de utilizator și parolă), astfel încât să se poată accesa informațiile din baza de date MySQL.+**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 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 }}\\ +**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 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''​. +{{ :​laboratoare:​laborator09:​activitate_de_laborator01.png?​nolink }} 
-    - 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()'';​ +{{ :​laboratoare:​laborator09:​activitate_de_laborator02.png?nolink }} 
-      * ''​cancelButtonHandler()''​; + 
-    - Să se specifice identificatori (proprietatea ''​fx:​id''​) pentru: +**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''​. 
-      * 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_laborator03.png?​nolink }} 
-    - Î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 }}\\  +<file xml authentication.xml>​ 
-  - Î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ă. +<!-- different imports --> 
-  - Î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. + 
-  - 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. +<​AnchorPane fx:​id="​AnchorPane"​ maxHeight="​-Infinity"​ maxWidth="​-Infinity"​ minHeight="​-Infinity"​ minWidth="​-Infinity"​  
-  ​- 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ă). +prefHeight="​128.0"​ prefWidth="​336.0000999999975"​  
-  - 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.+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 2236: 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]]
laboratoare/laborator09.1450383389.txt.gz · Last modified: 2015/12/17 22:16 by Andrei Roșu-Cojocaru
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0