IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 13 - Création de fenêtres & d'Applets

pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

Dans l'exemple suivant, toute l'intelligence concernant le dessin est contenue dans la classe SineDraw; la classe SineWave configure simplement le programme et le curseur. Dans SineDraw, la méthode setCycles() fournit un moyen pour permettre à un autre objet (le curseur dans ce cas) de contrôler le nombre de cycles.

//: c13:SineWave.java
// Dessin avec Swing, en utilisant un JSlider.
// <applet code=SineWave
//  width=700 height=400></applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;

class SineDraw extends JPanel {
  static final int SCALEFACTOR = 200;
  int cycles;
  int points;
  double[] sines;
  int[] pts;
  SineDraw() { setCycles(5); }
  public void setCycles(int newCycles) {
    cycles = newCycles;
    points = SCALEFACTOR * cycles * 2;
    sines = new double[points];
    pts = new int[points];
    for(int i = 0; i       double radians = (Math.PI/SCALEFACTOR) * i;
      sines[i] = Math.sin(radians);
    }
    repaint();
  }    
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    int maxWidth = getWidth();
    double hstep = (double)maxWidth/(double)points;
    int maxHeight = getHeight();
    for(int i = 0; i       pts[i] = (int)(sines[i] * maxHeight/2 * .95
                     + maxHeight/2);
    g.setColor(Color.red);
    for(int i = 1; i       int x1 = (int)((i - 1) * hstep);
      int x2 = (int)(i * hstep);
      int y1 = pts[i-1];
      int y2 = pts[i];
      g.drawLine(x1, y1, x2, y2);
    }
  }
}

public class SineWave extends JApplet {
  SineDraw sines = new SineDraw();
  JSlider cycles = new JSlider(1, 30, 5);
  public void init() {
    Container cp = getContentPane();
    cp.add(sines);
    cycles.addChangeListener(new ChangeListener(){
      public void stateChanged(ChangeEvent e) {
        sines.setCycles(
          ((JSlider)e.getSource()).getValue());
      }
    });
    cp.add(BorderLayout.SOUTH, cycles);
  }
  public static void main(String[] args) {
    Console.run(new SineWave(), 700, 400);
  }
} ///:~

Tous les membres de données et tableaux sont utilisés dans le calcul des points du sinus : cycles indique le nombre de périodes complètes de sinus désiré, points contient le nombre total de points qui sera tracé, sines contient les valeurs de la fonction sinus, et pts contient les coordonnées y des points qui seront tracés sur le JPanel. La méthode setCycles() construit le tableau selon le nombre de points nécessaires et remplit le tableau sines de valeurs. En appelant repaint(), setCycles force l'appel de paintComponent() , afin que le reste des calculs et le dessin aient lieu.

La première chose à faire lorsqu'on redéfinit paintComponent() est d'appeler la version de base de la méthode. Ensuite on peut faire ce que l'on veut; normalement cela signifie utiliser les méthodes de Graphics qu'on peut trouver dans la documentation de java.awt.Graphics (dans la documentation HTML de java.sun.com) pour dessiner et peindre des pixels sur le JPanel. On peut voir ici que la plupart du code concerne l'exécution des calculs, les deux seules méthodes qui manipulent effectivement l'écran sont setColor() et drawLine(). Vous aurez probablement la même sensation lorsque vous créerez votre propre programme d'affichage de données graphiques : vous passerez la plus grande partie du temps à déterminer ce qu'il faut dessiner, mais le dessin en lui-même sera assez simple.

Lorsque j'ai créé ce programme, j'ai passé le plus gros de mon temps à obtenir la courbe du sinus à afficher. Ceci fait, j'ai pensé que ce serait bien de pouvoir modifier dynamiquement le nombre de cycles. Mes expériences de programmation de ce genre de choses dans d'autres langages me rendaient un peu réticent, mais cette partie se révéla la partie la plus facile du projet. J'ai créé un JSlider (les arguments sont respectivement la valeur de gauche du JSlider, la valeur de droite, et la valeur initiale, mais il existe d'autres constructeurs) et je l'ai déposé dans le JApplet. Ensuite j'ai regardé dans la documentation HTML et j'ai vu que le seul listener était le addChangeListener, qui était déclenché chaque fois que le curseur était déplacé suffisamment pour produire une nouvelle valeur. La seule méthode pour cela était évidemment appelée stateChanged(), qui fournit un objet ChangeEvent, de manière à pouvoir rechercher la source de la modification et obtenir la nouvelle valeur.En appelant setCycles() des objets sines, la nouvelle valeur est prise en compte et le JPanel est redessiné.

En général, on verra que la plupart des problèmes Swing peuvent être résolus en suivant un processus semblable, et on verra qu'il est en général assez simple, même si on n'a pas utilisé auparavant un composant donné.

Si le problème est plus compliqué, il y a d'autres solutions plus sophistiquées, par exemple les composants JavaBeans de fournisseurs tiers, et l'API Java 2D. Ces solutions sortent du cadre de ce livre, mais vous devriez les prendre en considération si votre code de dessin devient trop coûteux.

Boîtes de dialogue

Une boîte de dialogue est une fenêtre qui est issue d'une autre fenêtre. Son but est de traiter un problème spécifique sans encombrer la fenêtre d'origine avec ces détails. Les boîtes de dialogue sont fortement utilisées dans les environnements de programmation à fenêtres, mais moins fréquemment utilisées dans les applets.

Pour créer une boîte de dialogue, il faut hériter de JDialog, qui est simplement une sorte de Window, comme les JFrames. Un JDialog possède un layout manager (qui est par défaut le BorderLayout) auquel on ajoute des listeners d'événements pour traiter ceux-ci. Il y a une différence importante : on ne veut pas fermer l'application lors de l'appel de windowClosing(). Au lieu de cela, on libère les ressources utilisées par la fenêtre de dialogue en appelant dispose(). Voici un exemple très simple :

//: c13:Dialogs.java
// Création et utilisation de boîtes de dialogue.
// <applet code=Dialogs width=125 height=75>
// </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;

class MyDialog extends JDialog {
  public MyDialog(JFrame parent) {
    super(parent, "My dialog", true);
    Container cp = getContentPane();
    cp.setLayout(new FlowLayout());
    cp.add(new JLabel("Here is my dialog"));
    JButton ok = new JButton("OK");
    ok.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        dispose(); // Ferme le dialogue
      }
    });
    cp.add(ok);
    setSize(150,125);
  }
}

public class Dialogs extends JApplet {
  JButton b1 = new JButton("Dialog Box");
  MyDialog dlg = new MyDialog(null);
  public void init() {
    b1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        dlg.show();
      }
    });
    getContentPane().add(b1);
  }
  public static void main(String[] args) {
    Console.run(new Dialogs(), 125, 75);
  }
} ///:~

Une fois le JDialog créé, la méthode show() doit être appelée pour l'afficher et l'activer. Pour que le dialogue se ferme, il faut appeler dispose().

On remarquera que tout ce qui sort d'une applet, y compris les boîtes de dialogue, n'est pas digne de confiance. C'est à dire qu'on obtient un avertissement dans la fenêtre qui apparaît. Ceci est dû au fait qu'en théorie il serait possible de tromper l'utilisateur et lui faire croire qu'il a à faire avec une de ses applications normales et de le faire taper son numéro de carte de crédit, qui partirait alors sur le Web. Une applet est toujours attachée à une page Web et visible dans un navigateur, tandis qu'une boîte de dialogue est détachée, et tout ceci est donc possible en théorie. Le résultat est qu'il n'est pas fréquent de voir une applet qui utilise une boîte de dialogue.

L'exemple suivant est plus complexe; la boîte de dialogue est composée d'une grille (en utilisant GridLayout) d'un type de bouton particulier qui est défini ici comme la classe ToeButton. Ce bouton dessine un cadre autour de lui et, selon son état, un blanc, un x ou un o au milieu. Il démarre en blanc, et ensuite, selon à qui c'est le tour, se modifie en x ou en o. Cependant, il transformera le x en o et vice versa lorsqu'on clique sur le bouton (ceci rend le principe du tic-tac-toe seulement un peu plus ennuyeux qu'il ne l'est déjà). De plus, la boîte de dialogue peut être définie avec un nombre quelconque de rangées et de colonnes dans la fenêtre principale de l'application.

//: c13:TicTacToe.java
// Démonstration de boîtes de dialogue
// et création de vos propres composants.
// <applet code=TicTacToe
//  width=200 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

public class TicTacToe extends JApplet {
  JTextField
    rows = new JTextField("3"),
    cols = new JTextField("3");
  static final int BLANK = 0, XX = 1, OO = 2;
  class ToeDialog extends JDialog {
    int turn = XX; // Démarre avec x a jouer
    // w = nombre de cellules en largeur
    // h = nombre de cellules en hauteur
    public ToeDialog(int w, int h) {
      setTitle("The game itself");
      Container cp = getContentPane();
      cp.setLayout(new GridLayout(w, h));
      for(int i = 0; i         cp.add(new ToeButton());
      setSize(w * 50, h * 50);
      // fermeture du dialogue en JDK 1.3 :
      //#setDefaultCloseOperation(
      //#  DISPOSE_ON_CLOSE);
      // fermeture du dialogue en JDK 1.2 :
      addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e){
          dispose();
        }
      });    
    }
    class ToeButton extends JPanel {
      int state = BLANK;
      public ToeButton() {
        addMouseListener(new ML());
      }
      public void paintComponent(Graphics g) {
        super.paintComponent(g);
        int x1 = 0;
        int y1 = 0;
        int x2 = getSize().width - 1;
        int y2 = getSize().height - 1;
        g.drawRect(x1, y1, x2, y2);
        x1 = x2/4;
        y1 = y2/4;
        int wide = x2/2;
        int high = y2/2;
        if(state == XX) {
          g.drawLine(x1, y1,
            x1 + wide, y1 + high);
          g.drawLine(x1, y1 + high,
            x1 + wide, y1);
        }
        if(state == OO) {
          g.drawOval(x1, y1,
            x1 + wide/2, y1 + high/2);
        }
      }
      class ML extends MouseAdapter {
        public void mousePressed(MouseEvent e) {
          if(state == BLANK) {
            state = turn;
            turn = (turn == XX ? OO : XX);
          }
          else
            state = (state == XX ? OO : XX);
          repaint();
        }
      }
    }
  }
  class BL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      JDialog d = new ToeDialog(
        Integer.parseInt(rows.getText()),
        Integer.parseInt(cols.getText()));
      d.setVisible(true);
    }
  }
  public void init() {
    JPanel p = new JPanel();
    p.setLayout(new GridLayout(2,2));
    p.add(new JLabel("Rows", JLabel.CENTER));
    p.add(rows);
    p.add(new JLabel("Columns", JLabel.CENTER));
    p.add(cols);
    Container cp = getContentPane();
    cp.add(p, BorderLayout.NORTH);
    JButton b = new JButton("go");
    b.addActionListener(new BL());
    cp.add(b, BorderLayout.SOUTH);
  }
  public static void main(String[] args) {
    Console.run(new TicTacToe(), 200, 100);
  }
} ///:~

Comme les statics peuvent être uniquement au niveau le plus extérieur de la classe, les classes internes ne peuvent pas avoir de données static ni de classes internes static.

La méthode paintComponent() dessine le carré autour du panneau, et le x ou le o. C'est rempli de calculs fastidieux, mais c'est direct.

Les clics de souris sont capturés par le MouseListener, qui vérifie d'abord si le panneau a déjà quelque chose d'écrit sur lui. Si ce n'est pas le cas, on recherche la fenêtre parente pour déterminer à qui est le tour, et on positionne l'état du ToeButton en conséquence. Le ToeButton retrouve le parent par le mécanisme de la classe interne, et passe au tour suivant. Si le bouton affiche déjà un x ou un o, son affichage est inversé. On peut voir dans ces calculs l'usage pratique du if-else ternaire décrit au Chapitre 3. On repeint le ToeButton chaque fois qu'il change d'état.

Le constructeur de ToeDialog est assez simple : il ajoute à un GridLayout autant de boutons que demandé, puis redimensionne chaque bouton à 50 pixels.

TicTacToe installe l'ensemble de l'application par la création de JTextFields (pour entrer le nombre de rangées et colonnes de la grille de boutons) et le bouton «go» avec son ActionListener. Lorsqu'on appuie sur le bouton, les données dans les JTextFields doivent être récupérées et, puisqu'elles sont au format String, transformées en ints en utilisant la méthode statique Integer.parseInt().

Dialogues pour les fichiers [File dialogs]

Certains systèmes d'exploitation ont des boîtes de dialogue standard pour gérer certaines choses telles que les fontes, les couleurs, les imprimantes, et cetera. En tout cas, pratiquement tous les systèmes d'exploitation graphiques fournissent les moyens d'ouvrir et de sauver les fichiers, et le JFileChooser de Java les encapsule pour une utilisation facile.

L'application suivante utilise deux sortes de dialogues JFileChooser, un pour l'ouverture et un pour la sauvegarde. La plupart du code devrait maintenant être familière, et toute la partie intéressante se trouve dans les action listeners pour les différents clics de boutons :

//: c13:FileChooserTest.java
// Démonstration de boîtes de dialogues de fichiers.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

public class FileChooserTest extends JFrame {
  JTextField
    filename = new JTextField(),
    dir = new JTextField();
  JButton
    open = new JButton("Open"),
    save = new JButton("Save");
  public FileChooserTest() {
    JPanel p = new JPanel();
    open.addActionListener(new OpenL());
    p.add(open);
    save.addActionListener(new SaveL());
    p.add(save);
    Container cp = getContentPane();
    cp.add(p, BorderLayout.SOUTH);
    dir.setEditable(false);
    filename.setEditable(false);
    p = new JPanel();
    p.setLayout(new GridLayout(2,1));
    p.add(filename);
    p.add(dir);
    cp.add(p, BorderLayout.NORTH);
  }
  class OpenL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      JFileChooser c = new JFileChooser();
      // Démontre le dialogue "Open" :
      int rVal =
        c.showOpenDialog(FileChooserTest.this);
      if(rVal == JFileChooser.APPROVE_OPTION) {
        filename.setText(
          c.getSelectedFile().getName());
          dir.setText(
            c.getCurrentDirectory().toString());
      }
      if(rVal == JFileChooser.CANCEL_OPTION) {
        filename.setText("You pressed cancel");
        dir.setText("");
      }
    }
  }
  class SaveL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      JFileChooser c = new JFileChooser();
      // Démontre le dialogue "Save" :
      int rVal =
        c.showSaveDialog(FileChooserTest.this);
      if(rVal == JFileChooser.APPROVE_OPTION) {
        filename.setText(
          c.getSelectedFile().getName());
          dir.setText(
            c.getCurrentDirectory().toString());
      }
      if(rVal == JFileChooser.CANCEL_OPTION) {
        filename.setText("You pressed cancel");
        dir.setText("");
      }
    }
  }
  public static void main(String[] args) {
    Console.run(new FileChooserTest(), 250, 110);
  }
} ///:~

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par P. Boite ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java