Graphical User Interface (GUI) - Java Swing și JavaFX

De la WikiLabs
Versiunea din 28 august 2012 10:01, autor: Radu Hobincu (Discuție | contribuții) (JavaFX)

În multe dintre aplicațiile pe care le veți dezvolta folosing Java, veți avea nevoie și de o interfață grafică adecvată: ferestre, butoane, câmpuri de editare, checkbox­uri, etc. Există mai multe biblioteci de dezvoltare pentru interfețe grafice, cea mai cunoscută fiind Java Swing și cea mai nouă fiind JavaFX.

Java Swing

Pachetul care conține majoritatea claselor pentru aplicațiile Swing este java.swing dar sunt utilizate și multe din clasele din vechea biblotecă GUI, java.awt. Clasa principală pentru aplicații Java Swing este clasa javax.swing.JFrame.

Frame-ul este fereastra principală a unei interfețe grafice. Este elementul care are asociată bara de deasupra, care conține icon-ul, numele aplicației și cele trei butoane: minimize, maximize, close:

Exemplu de frame fără elemente

În continuare este prezentat un frame ce conține cele mai utilizate componente Swing:

Exemplu de frame cu elemente

Componente

Toate obiectele Swing, cu excepția clasei JFrame, moștenesc clasa javax.swing.JComponent, care la rândul ei moștenește (indirect) clasa java.awt.Container. Astfel, se creează o ierarhie de componente, fiecare element (numit parent) înglobând alte sub-componente (numite children). În continuare se vor prezenta elementele necesare realizării temei 6. Pentru descrierea altor elemente mai avansate, citiți tutorial-ul Oracle.

javax.swing.JPanel

JPanel este un container generic care poate conține alte elemente. Poate fi vizibil, schimbându-i-se culoarea background-ului, sau modelul marginii, sau invizibil, folosit doar pentru ierarhizarea conținutului. Este indicat să nu plasați alte obiecte direct pe un JFrame, ci doar un JPanel care să conțină restul de elemente. Acest lucru este foarte util și când aveți nevoie să schimbați complet elementele dintr-un JFrame, înlocuiți doar JPanel-ul.

javax.swing.JLabel

JLabel este un component utilizat pentru a afișa text sau imagini într-un container.

javax.swing.JButton

JButton este, cum îi spune și numele, un buton. Acesta poate avea afișat un text sau o imagine. De cele mai multe ori este utilizat asociindu-i-se un Event Handler de tip ActionListener (vezi #Event Handlers) care se declanșează când acesta este apăsat (se dă click pe el).

javax.swing.JTextField

JTextField reprezintă o zonă în care se poate introduce text scurt, de o singură linie. Și acestul element i se poate asocia un Event Handler de tip ActionListener care se declanșează când se apasă tasta Enter în zona de editare.

javax.swing.JTextArea

JTextArea reprezintă o zonă în care se poate introduce text de mai multe linii.

Layouts

O altă serie de clase necesară pentru implementarea unei interfețe grafice este setul de clase care extind interfața java.awt.LayoutManager. Aceste clase descriu modul în care elementele se așează într-un Container. Cele mai importante sunt:

java.awt.FlowLayout

FlowLayout este utilizat pentru a așseza elementele pe orizontală, până când nu mai încap, în care situație se trece pe rândul următor. Acesta este cel mai simplu tip de layout.

java.awt.GridLayout

GridLayout este utilizat pentru a așseza elementele într-o matrice cu număr configurabil de linii și coloane.

java.awt.BorderLayout

BorderLayout este utilizat pentru a așseza elementele dealungul celor patru margini și în centru, adică NORTH, SOUTH, EAST, WEST și CENTER. Un singur element poate fi plasat în fiecare din aceste poziții.

java.awt.CardLayout

CardLayout este utilizat pentru a adăuga mai multe componente unui Container din care doar unul este vizibil la un moment dat. Acest tip de layout este util pentru generarea de aplicații tip wizard în care utilizatorul trece de la o fereastră la alta cu ajutorul unor butoane Next și Previous.

Mai multe despre elemente de tip Layout, în tutorial-ul Oracle.

Event Handlers

Fiecare JComponent suportă o listă de evenimente la care e sensibil. Fiecărui eveniment i se poate asociaza o acțiune care se execută când acel eveniment se declanșează. De exemplu, când un buton este apăsat, sau când cursorul de la mouse a intrat în zona ocupată de componentă, sau când s-a tastat ceva într-o zonă de text, etc. Aceste handler-e sunt, de fapt, niște metode, definite în anumite interfețe. Aceste metode se excută în paralel cu programul principal (ca și thread-uri) în momentul în care evenimentul se declanșează. Metodele care adaugă un handler unui obiect sunt de forma:

  • public void addActionListener(ActionListener _listener) - pentru evenimente de tip action, adică activarea unui component (click pe un buton, enter într-un text field, etc.);
  • public void addMouseListener(MouseListener _listener) - pentru evenimente legate de mouse;
  • etc.

Același handler poate fi asociat mai multor elemente, iar în acest caz, pentru a știi care obiect a generat evenimentul, se folosește metoda getSource() definită în clasa java.util.EventObject care este superclasă pentru toate obiectele primite ca argumente de metode de tip handler (java.awt.event.ActionEvent, java.awt.event.MouseEvent, etc.). Metoda getSource() întoarce o referință de tip Object la componenta de interfață care a generat evenimentul.

Mai multe despre handler-e, în tutorial-ul Oracle.

Exemplu

Vom descrie un JFrame care conține două JLabel, două JTextField, un JTextArea și un JButton. Când se apasă butonul, cele două JTextField se vor încărca cu valorile dimensiunii JFrame-ului și se va scrie un caracter x în JTextArea:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class FrameTest extends JFrame implements ActionListener {

    // we define the elements used in
    // the frame
    private JPanel mainPanel;
    private JLabel xLabel;
    private JLabel yLabel;
    private JTextField xField;
    private JTextField yField;
    private JTextArea textArea;
    private JButton button;
    
public FrameTest(String _title){
    // calling the constructor for JFrame to set the title
    super(_title);
    
    // initializing the components (method is implemented below)
    initComponents();
    
    // calling pack() defined in superclass to resize the frame according to
    // contents
    pack();
    
    // displaying the frame
    setVisible(true);
    
    // select the default behaviour when closing the frame by clicking
    // the X button on the bar: the application will exit
    setDefaultCloseOperation(EXIT_ON_CLOSE);
}

public static void main(String[] _args){
    FrameTest _frame = new FrameTest("Frame Test");
}

private void initComponents() {
    // setting the layout as FlowLayout
    setLayout(new FlowLayout());
    
    // creating the components
    
    // the panel adds the components in
    // a FlowLayout
    mainPanel = new JPanel(new FlowLayout()); 
    xLabel = new JLabel("X size (width):");
    yLabel = new JLabel("Y size (height):");
    xField = new JTextField(6); //6 columns (characters)
    yField = new JTextField(6); //6 columns (characters)
    textArea = new JTextArea(10, 10); //10 columns, 10 rows
    button = new JButton("Click me!");
    
   
    //adding components to panel
    mainPanel.add(xLabel);
    mainPanel.add(xField);
    mainPanel.add(yLabel);
    mainPanel.add(yField);
    mainPanel.add(textArea);
    mainPanel.add(button);
    
    //adding scrollPane to frame
    add(mainPanel);
    
    // adding the listener to the button Component
    button.addActionListener(this);
}

// this is the handler defined in the ActionListener interface
public void actionPerformed(ActionEvent _actionEvent) {
    xField.setText(String.valueOf(this.getWidth()));
    yField.setText(String.valueOf(this.getHeight()));
    textArea.append("x");
}
    
}

Rezultatul arată așa:

Codul de mai sus, rulat

Pentru a crea frame-uri mai complexe, trebuie să folosiți alte tipuri de LayoutManager, elemente de tip javax.swing.JScrollPane, sau, puteți utiliza un mediu de dezvoltare, gen Netbeans pentru a crea în mod vizual interfața.

JavaFX

JavaFX este cea mai nouă implementare a platformei de dezvoltare pentru interfețe GUI de client. Cu toate că este încă într-un stadiu incipient, are avantaje față de vechiul Swing. Versiunea 2.2, care a fost publicată pe 14 august 2012 este un pas înainte către o nouă generație de aplicații multimedia pe Internet. Conform site-ului Oracle, principalele avantaje ale JavaFX sunt:

  • integrare completă cu Java SE și JDK, incepând cu versiunea 7, update 6 (7u6), ceea ce implică faptul că aplicațiile JavaFX vor putea fi dezvoltate și rulate de către orice client cu această verisune de Java;
  • inițial JavaFX a fost un limbaj de scripting, dar acum Oracle pune la dispoziție un API pentru dezvoltarea aplicațiilor direct în Java, pentru un mai bun management și reutilizare a codului;
  • un nou motor (engine) grafic, numit Prism, care face uz de accelerarea hardware oferită de GPU-urile moderne, precum și un nou manager de ferestre (Window Toolkit) numit Glass;
  • un nou limbaj bazat pe XML, numit FXML, folosit pentru descrierea interfețelor grafice, astfel încât să nu fie nevoie de recompilarea codului la fiecare modificare;
  • un nou engine multimedia, bazat pe GStreamer, care permite redarea de conținut multimedia;
  • o componentă care poate afișa conținut web și care poate fi integrată în orice interfață grafică JavaFX;
  • o serie de componente noi de interfață, precum grafice, tabele, meniuri și panouri;
  • un sistem de a împacheta aplicațiile astfel încât acestea să fie livrate cu toate bibliotecile necesare execuției;
  • portabilitate pe Linux, Windows și Mac OS X;

Un alt avantaj alt JavaFX spre deosebire de Swing, este faptul că aceeași aplicație poate fi rulată de sine stătător, ca applet, sau ca aplicație de tip Web Start.

Atenție: În laboratorul din sala 2 de calculatoare este instalată o versiune veche de Java care nu permite dezvoltarea aplicațiilor in JavaFX.

Componente

Ca și Java Swing, JavaFX se bazează pe o ierarhie de clase care implementează diferite componente și containere ce reprezintă elementele grafice. Analog clasei JFrame, în JavaFX, clasa care descrie fereastra principală a unei aplicații este javafx.stage.Stage. O aplicație JavaFX (spre deosebire de o aplicație Java obișnuită, care pornește cu metoda main()) trebuie să extindă clasa javafx.application.Application. Aceasta este o clasă abstractă, deci utilizatorul este obligat să definească metoda public void start(Stage _primaryStage), care este metoda de start a aplicației, analog metodei main().

Un obiect de tip Stage conține, la un moment dat, o singură scenă (javafx.scene.Scene). Această scenă este inițializată dându-i-se dimensiunile scenei și container-ul care conține toate celălalte elemente din fereastră. Acest container este, de cele mai multe ori, un panou. Panoul, pe lângă rolul de container, specifică și modul în care sunt afișate componentele, analog LayoutManager-ului din Swing. Vom folosi, pentru exemplu, un javafx.scene.layout.FlowPane, care este analog clasei FlowLayout:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class JavaFXApplicationTest extends Application {

public void start(Stage _primaryStage){
    // creating the pane for the elements:
    FlowPane _pane = new FlowPane();

    // creating the scene with the FlowPane as the
    // container for all elements and dimensions
    // width = 300 and height = 250 pixels
    Scene scene = new Scene(_pane, 300, 250);

    // setting the scene in the primary stage
    primaryStage.setScene(scene);

    // setting the title of the stage
    primaryStage.setTitle("Empty pane");

    //displaying the stage
    primaryStage.show();
}

/**
 * The main() method is ignored in correctly deployed JavaFX application.
 * main() serves only as fallback in case the application can not be
 * launched through deployment artifacts, e.g., in IDEs with limited FX
 * support. NetBeans ignores main().
 */
public static void main(String[] args) {
    launch(args);
}

}

Rezultatul:

O scenă fără elemente în JavaFX

Alte componente care pot fi folosite într-un scene:

Un exemplu analog cu cel de la Swing:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class JavaFXApplicationTest extends Application {
    
    private Label labelx;
    private Label labely;
    private TextField fieldx;
    private TextField fieldy;
    private TextArea area;
    private Button button;
    
public void start(Stage primaryStage) {

    FlowPane _pane = new FlowPane();

    //creating the components:
    labelx = new Label("Width:");
    labely = new Label("Height:");
    fieldx = new TextField();
    fieldy = new TextField();
    area = new TextArea();
    button = new Button("Click me!");

    //adding the elements to pane:
    _pane.getChildren().add(labelx);
    _pane.getChildren().add(fieldx);
    _pane.getChildren().add(labely);
    _pane.getChildren().add(fieldy);
    _pane.getChildren().add(area);
    _pane.getChildren().add(button);
    
    Scene scene = new Scene(_pane, 300, 250);

    primaryStage.setTitle("Test pane");
    primaryStage.setScene(scene);
    primaryStage.show();
}

/**
 * The main() method is ignored in correctly deployed JavaFX application.
 * main() serves only as fallback in case the application can not be
 * launched through deployment artifacts, e.g., in IDEs with limited FX
 * support. NetBeans ignores main().
 */
public static void main(String[] args) {
    launch(args);
}

}

Cu rezultatul:

O scenă cu diverse elemente în JavaFX

Panouri

Analog diferitelor tipuri de LayoutManager din Swing și JavaFX dispune de o serie de panouri:

Tot panouri sunt și alte tipuri de elemente care în Java Swing aveau propriul tip de clasă, doar că aceste nu fac parte din pachetul javafx.scene.layout ci javafx.scene.control: