A31 - Conception et programmation objet avancées

4 - Java Swing

Adrien Krähenbühl IUT Robert Schuman

Les interfaces graphiques en Java

1995
AWT (Abstract Windows Toolkit) permet de créer des UI (User Interfaces) en exploitant le système graphique de l’OS.
1998
Swing étend AWT pour créer des UI identiques quel que soit l’OS.
2014
JavaFX gère animations et médias. Les interfaces sont décrites en XML et peuvent s’adapter au type d’écran : ordinateur, mobile, etc.

Pourquoi Swing plutôt que JavaFX ?

Swing est :

  • plus simple à prendre en main
  • un projet plus mature
  • largement majoritaire en entreprise (pour l’instant)

JavaFX n’est pas intégré au JDK :

  • installation en plus de Java et configuration nécessaire
  • une archive .jar doit embarquer JavaFX

Une première fenêtre

Les composants graphiques

Une première fenêtre

public static void main( String[] args ) {
    new MainWindow();
}
import javax.swing.JFrame;

public class MainWindow extends JFrame {

    public MainWindow() {
        setTitle( "My App" );
        setSize( 400, 500 );
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible( true );
    }

}

setSize : Dimensions de la fenêtre (hauteur,largeur) setDefaultCloseOperation : Choix de l’action lors du clic sur la croix setVisible : Rend la fenêtre (in)visible. Elle est invisible par défaut.

Ajouter du contenu

import javax.swing.JFrame;
import javax.swing.JLabel;

public class MainWindow extends JFrame {

    public MainWindow() {
        setTitle( "My App" );
        setSize( 400, 500 );
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JLabel texte = new JLabel("Mon texte");
        add( texte );

        setVisible( true );
    }

}

add(Component component)
Ajoute un composant à la JFrame.

Ajouter plus de contenu ?

import javax.swing.JFrame;
import javax.swing.JLabel;

public class MainWindow extends JFrame {

    public MainWindow() {
        setTitle( "My App" );
        setSize( 400, 500 );
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JLabel texte = new JLabel("Mon texte");
        add( texte );
        JLabel texte2 = new JLabel("Mon autre texte");
        add( texte2 );

        setVisible( true );
    }

}

Ça ne fonctionne pas comme ça !

Layouts

Anatomie d’une fenêtre

Une JFrame contient un seul élément, le “Content Pane” :

  • qui peut être n’importe quel Container
  • qui est par défaut un JPanel organisé avec un BorderLayout

La méthode add de JFrame ajoute les éléments à son “Content Pane”.

JPanel et layouts

Un JPanel :

  • est le conteneur de composants générique de Swing.
  • utilise un gestionnaire de mise en page (layout manager) pour organiser ses composants.

Il existe 8 “Layout Manager” utilisables :

BorderLayout

public MainWindow() {
    setTitle( "My App" );
    setSize( 400, 500 );
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JButton button1 = new JButton("Button 1...");
    JButton button2 = new JButton("Button 2...");
    JButton button3 = new JButton("Button 3...");
    JButton button4 = new JButton("Long-Named...");
    JButton button5 = new JButton("5 (LINE_END)");

    JPanel panel = new JPanel();
    panel.setLayout( new BorderLayout() );
    panel.add( button1, BorderLayout.NORTH );
    panel.add( button2, BorderLayout.CENTER );
    panel.add( button3, BorderLayout.WEST );
    panel.add( button4, BorderLayout.SOUTH );
    panel.add( button5, BorderLayout.EAST );
    setContentPane( panel );

    setVisible( true );
}

BorderLayout comporte 5 zones de placement : NORTH,SOUTH, WEST, EAST et CENTER (ou PAGE_START, PAGE_END, LINE_START, LINE_END et CENTER).

FlowLayout

JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JButton button3 = new JButton("Button 3");
JButton button4 = new JButton("Long-Named Button 4");
JButton button5 = new JButton("5");

JPanel panel = new JPanel();
panel.setLayout( new FlowLayout() );
panel.add( button1 );
panel.add( button2 );
panel.add( button3 );
panel.add( button4 );
panel.add( button5 );

setContentPane( panel );
  • FlowLayout permet de placer des éléments en ligne dans le JPanel.
  • Le éléments reviennent à la ligne automatiquement.
  • Il est possible de choisir de les aligner à gauche ou à droite.

BoxLayout

JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JButton button3 = new JButton("Button 3");
JButton button4 = new JButton("Long-Named Button 4");
JButton button5 = new JButton("5");

button1.setAlignmentX(Component.CENTER_ALIGNMENT);
// Alignement à répéter pour les 5 boutons

JPanel panel = new JPanel();
panel.setLayout( new BoxLayout(panel,BoxLayout.Y_AXIS) );
panel.add( button1 );
panel.add( button2 );
panel.add( button3 );
panel.add( button4 );
panel.add( button5 );

setContentPane( panel );

  • BoxLayout permet de placer des éléments en ligne ou en colonne.
  • Il n’y a pas de retour à la ligne automatique
  • Les composants choisissent eux-même leur alignement dans le BoxLayout dans l’axe opposé à la direction principale.

GridLayout

JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JButton button3 = new JButton("Button 3");
JButton button4 = new JButton("Long-Named Button 4");
JButton button5 = new JButton("5");

JPanel panel = new JPanel();
panel.setLayout( new GridLayout(0,2) );
panel.add( button1 );
panel.add( button2 );
panel.add( button3 );
panel.add( button4 );
panel.add( button5 );

setContentPane( panel );

  • GridLayout permet de placer des éléments dans une grille.
  • Le constructeur prend le nombre de lignes et de colonnes en paramètres. Un paramètre à 0 signifie “n’importe quel nombre”.
  • Les composants occupent une place identique dans la grille.
  • Tout l’espace est occupé.

GridBagLayout

JPanel panel = new JPanel();
panel.setLayout( new GridBagLayout() );

GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 0;
panel.add( button1, constraints );
constraints.gridx = 1;
panel.add( button2, constraints );
constraints.gridx = 2;
panel.add( button3, constraints );
constraints.gridwidth = 3;
constraints.gridx = 0;
constraints.gridy = 1;
panel.add( button4, constraints );
constraints.gridwidth = 2;
constraints.gridx = 1;
constraints.gridy = 2;
constraints.insets = new Insets(10,0,0,0); //top padding
panel.add( button5,constraints );
  • GridBag positionne chaque élément avec des contraintes
  • Il y a 11 contraintes possibles qui peuvent être combinées
  • GridBag peut potentiellement simuler tous les autres Layouts

Autres Layout





Card Layout


GroupLayout

  


SpringLayout



À vous d’aller découvrir les autres layouts !

Des layouts, des composants… et ensuite ?



C’est bien joli d’avoir une interfaces, mais comment je fais pour réagir aux clics et autres évènements de l’utilisateur ?

Les évènements

Du texte, un bouton

public class MainWindow extends JFrame {
    public MainWindow() {
        setTitle( "My App" );
        setSize( 180, 100 );
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JLabel text = new JLabel("Ici le texte");
        JButton button1 = new JButton("Cliquez ici");

        add(text,BorderLayout.CENTER);
        add(button1,BorderLayout.SOUTH);

        setVisible( true );
    }
}

Problème :
Comment réagir au clic sur le bouton pour modifier le texte ?


Solution : Avec le design pattern Observer !

EventListener, l’Observer de Swing

Rappel du Design pattern Observer.

La base des EventListener de Swing.

Les classes qui émettent des ActionEvent

À vous de créer vos observateurs écouteurs !

Exemple d’ActionListener

public MainWindow() {
    setTitle( "My App" );
    setSize( 180, 100 );
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JLabel text = new JLabel("Ici le texte");
    JButton button1 = new JButton("Cliquez ici");

    class MaClasse implements ActionListener {
        @Override
        public void actionPerformed( ActionEvent actionEvent ) {
            text.setText( "Le bouton a été cliqué !" );
        }
    }

    button1.addActionListener( new MaClasse() );

    add(text,BorderLayout.CENTER);
    add(button1,BorderLayout.SOUTH);

    setVisible( true );
}
public MainWindow() {
    setTitle( "My App" );
    setSize( 180, 100 );
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JLabel text = new JLabel("Ici le texte");
    JButton button1 = new JButton("Cliquez ici");

    button1.addActionListener( new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent actionEvent ) {
            text.setText( "Le bouton a été cliqué !" );
        }
    } );



    add(text,BorderLayout.CENTER);
    add(button1,BorderLayout.SOUTH);

    setVisible( true );
}


Avant clic
Après clic


Avec une classe interne.
Mieux : Avec une classe anonyme.

Utiliser le paramètre ActionEvent

public MainWindow() {
    setTitle( "My App" );
    setSize( 180, 100 );
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JLabel text = new JLabel("Ici le texte");
    JButton button1 = new JButton("Cliquez ici");

    button1.addActionListener( new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent actionEvent ) {
            button1.setText( "J'ai été cliqué !" );
        }
    } );



    add(text,BorderLayout.CENTER);
    add(button1,BorderLayout.SOUTH);

    setVisible( true );
}
public MainWindow() {
    setTitle( "My App" );
    setSize( 180, 100 );
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JLabel text = new JLabel("Ici le texte");
    JButton button1 = new JButton("Cliquez ici");

    button1.addActionListener( new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent actionEvent ) {
            JButton button = (JButton) actionEvent.getSource();
            button.setText( "J'ai été cliqué !" );
        }
    } );


    add(text,BorderLayout.CENTER);
    add(button1,BorderLayout.SOUTH);

    setVisible( true );
}


Avant clic
Après clic


Modifier le texte du bouton.
Mieux : modifier le texte de l’objet qui a été cliqué.

Bonne pratique dans un contexte MVC

public MainWindow( Controller controller ) {
    ...
    JButton button = new JButton("Valider");
    button.addActionListener( new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent actionEvent ) {
            //****************************************
            //* APPELLER UNE MÉTHODE D'UN CONTROLEUR !
            //****************************************
            controller.doSomething();
        }
    } );
    ...
}


Rappel
Une vue ne doit jamais modifier le modèle, elle demande au contrôleur de le faire.

Et maintenant…



À vous de jouer développer !