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 

Pour un dialogue d'ouverture de fichier, on appelle showOpenDialog(), et pour un dialogue de sauvegarde de fichier, on appelle showSaveDialog(). Ces commandes ne reviennent que lorsque le dialogue est fermé. L'objet JFileChooser existe encore, de sorte qu'on peut en lire les données. Les méthodes getSelectedFile() et getCurrentDirectory() sont deux façons d'obtenir les résultats de l'opération. Si celles-ci renvoient null, cela signifie que l'utilisateur a abandonné le dialogue.

HTML sur des composants Swing

Tout composant acceptant du texte peut également accepter du texte HTML, qu'il reformatera selon les règles HTML. Ceci signifie qu'on peut très facilement ajouter du texte de fantaisie à un composant Swing. Par exemple :

//: c13:HTMLButton.java
// Mettre du texte HTML sur des composants Swing.
// <applet code=HTMLButton width=200 height=500>
// </applet>
importjavax.swing.*;
importjava.awt.event.*;
importjava.awt.*;
importcom.bruceeckel.swing.*;

publicclassHTMLButton extendsJApplet {
JButton b = newJButton("<html><b><font size=+2>"+
   "<center>Hello!<br><i>Press me now!");
publicvoidinit() {
   b.addActionListener(newActionListener() {
     publicvoidactionPerformed(ActionEvent e){
       getContentPane().add(newJLabel("<html>"+
         "<i><font size=+4>Kapow!"));
       // Force un réalignement pour
       // inclure le nouveau label:
       validate();
     }
   });
   Container cp = getContentPane();
   cp.setLayout(newFlowLayout());
   cp.add(b);
}
publicstaticvoidmain(String[] args) {
   Console.run(newHTMLButton(), 200, 500);
}
} ///:~

Le texte doit commencer avec , et ensuite on peut utiliser les tags HTML normaux. Remarquons que les tags de fermeture ne sont pas obligatoires.

L'ActionListener ajoute au formulaire un nouveau JLabel contenant du texte HTML. Comme ce label n'est pas ajouté dans la méthode init(), on doit appeler la méthode validate() du conteneur de façon à forcer une redisposition des composants (et de ce fait un affichage du nouveau label).

On peut également ajouter du texte HTML à un JTabbedPane, JMenuItem, JToolTip, JRadioButton ou un JCheckBox.

Curseurs [sliders] et barres de progression [progress bars]

Un slider(qu'on a déjà utilisé dans l'exemple du sinus) permet à l'utilisateur de rentrer une donnée en déplaçant un point en avant ou en arrière, ce qui est intuitif dans certains cas (un contrôle de volume, par exemple). Un progress bar représente une donnée en remplissant proportionnellement un espace vide pour que l'utilisateur ait une idée de la valeur. Mon exemple favori pour ces composants consiste à simplement lier le curseur à la barre de progression, de sorte que lorsqu'on déplace le curseur la barre de progression change en conséquence :

//: c13:Progress.java
// Utilisation de progress bars et de sliders.
// <applet code=Progress
//  width=300 height=200></applet>
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.event.*;
importjavax.swing.border.*;
importcom.bruceeckel.swing.*;

publicclassProgress extendsJApplet {
JProgressBar pb = newJProgressBar();
JSlider sb =
   newJSlider(JSlider.HORIZONTAL, 0, 100, 60);
publicvoidinit() {
   Container cp = getContentPane();
   cp.setLayout(newGridLayout(2,1));
   cp.add(pb);
   sb.setValue(0);
   sb.setPaintTicks(true);
   sb.setMajorTickSpacing(20);
   sb.setMinorTickSpacing(5);
   sb.setBorder(newTitledBorder("Slide Me"));
   pb.setModel(sb.getModel()); // Partage du modèle
   cp.add(sb);
}
publicstaticvoidmain(String[] args) {
   Console.run(newProgress(), 300, 200);
}
} ///:~

La clé du lien entre les deux composant se trouve dans le partage de leur modèle, dans la ligne :

pb.setModel(sb.getModel());

Naturellement, on pourrait aussi contrôler les deux composants en utilisant un listener, mais ceci est plus direct pour les cas simples.

Le JProgressBar est assez simple, mais le JSlidera un grand nombre d'options, telles que l'orientation et les graduations mineures et majeures. Remarquons la simplicité de l'ajout d'une bordure avec titre.

Arbres [Trees]

L'utilisation d'un JTreepeut être aussi simple que ceci :

add(newJTree(
newObject[] {"this", "that", "other"}));

Ceci affiche un arbre rudimentaire. L'API pour les arbres est vaste, probablement une des plus importantes de Swing. On peut faire à peu près tout ce qu'on veut avec des arbres, mais les tâches plus complexes demandent davantage de recherches et d'expérimentations.

Heureusement, il y a un niveau intermédiaire fourni dans la bibliothèque : les composants arbres par défaut, qui font en général ce dont on a besoin, de sorte que la plupart du temps on peut se contenter de ces composants, et ce n'est que dans des cas particuliers qu'on aura besoin d'approfondir et de comprendre plus en détail les arbres.

L'exemple suivant utilise les composants arbres par défaut pour afficher un arbre dans une applet. Lorsqu'on appuie sur le bouton, un nouveau sous-arbre est ajouté sous le noeud sélectionné (si aucun noeud n'est sélectionné, le noeud racine est utilisé) :

//: c13:Trees.java
// Exemple d'arbre Swing simple. Les arbres peuvent
// être beaucoup plus complexes que celui-ci.
// <applet code=Trees
//  width=250 height=250></applet>
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.tree.*;
importcom.bruceeckel.swing.*;

// Prend un tableau de Strings et crée un noeud à partir
// du premier élément, et des feuilles avec les autres :
classBranch {
DefaultMutableTreeNode r;
publicBranch(String[] data) {
   r = newDefaultMutableTreeNode(data[0]);
   for(inti = 1; i      r.add(newDefaultMutableTreeNode(data[i]));
}
publicDefaultMutableTreeNode node() {
   returnr;
}
}  

publicclassTrees extendsJApplet {
String[][] data = {
   { "Colors", "Red", "Blue", "Green"},
   { "Flavors", "Tart", "Sweet", "Bland"},
   { "Length", "Short", "Medium", "Long"},
   { "Volume", "High", "Medium", "Low"},
   { "Temperature", "High", "Medium", "Low"},
   { "Intensity", "High", "Medium", "Low"},
};
staticinti = 0;
DefaultMutableTreeNode root, child, chosen;
JTree tree;
DefaultTreeModel model;
publicvoidinit() {
   Container cp = getContentPane();
   root = newDefaultMutableTreeNode("root");
   tree = newJTree(root);
   // On l'ajoute et on le rend scrollable :
   cp.add(newJScrollPane(tree),
     BorderLayout.CENTER);
   // Obtention du modèle de l'arbre :
   model =(DefaultTreeModel)tree.getModel();
   JButton test = newJButton("Press me");
   test.addActionListener(newActionListener() {
     publicvoidactionPerformed(ActionEvent e){
       if(i          child = newBranch(data[i++]).node();
         // Quel est le dernier élément cliqué ?
         chosen = (DefaultMutableTreeNode)
           tree.getLastSelectedPathComponent();
         if(chosen ==null) chosen = root;
         // Le modèle créera l'événement approprié.
         // En réponse, l'arbre se remettra à jour :
         model.insertNodeInto(child, chosen, 0);
         // Ceci place le nouveau noeud
         // sur le noeud actuellement sélectionné.
       }
     }
   });
   // Change les couleurs des boutons :
   test.setBackground(Color.blue);
   test.setForeground(Color.white);
   JPanel p = newJPanel();
   p.add(test);
   cp.add(p, BorderLayout.SOUTH);
}
publicstaticvoidmain(String[] args) {
   Console.run(newTrees(), 250, 250);
}
} ///:~

La première classe, Branch, est un outil qui prend un tableau et construit un DefaultMutableTreeNodeavec la première String comme racine, et le reste des Strings du tableau pour les feuilles. Ensuite node() peut être appelé pour créer la racine de cette branche.

La classe Trees contient un tableau de Strings à deux dimensions à partir duquel des Branches peuvent être créées, ainsi qu'un static int i pour servir d'index à travers ce tableau. L'objet DefaultMutableTreeNode contient les noeuds, mais la représentation physique à l'écran est contrôlée par le JTree et son modèle associé, le DefaultTreeModel. Notons que lorsque le JTree est ajouté à l'applet, il est encapsulé dans un JScrollPane : c'est suffisant pour permettre un scrolling automatique.

Le JTree est contrôlé par son modèle. Lorsqu'on modifie les données du modèle, celui-ci génère un événement qui force le JTree à effectuer les mises à jour nécessaires de la partie visible de la représentation de l'arbre. Dans init(), le modèle est obtenu par appel à getModel(). Lorsqu'on appuie sur le bouton, une nouvelle branche est créée. Ensuite le composant actuellement sélectionné est recherché (on utilise la racine si rien n'est sélectionné) et la méthode insertNodeInto() du modèle effectue la modification de l'arbre et provoque sa mise à jour.

Un exemple comme ci-dessus peut vous donner ce dont vous avez besoin pour utiliser un arbre. Cependant les arbres ont la possibilité de faire à peu près tout ce qui est imaginable ; chaque fois que le mot default apparaît dans l'exemple ci-dessus, on peut y substituer sa propre classe pour obtenir un comportement différent. Mais attention : la plupart de ces classes a une interface importante, de sorte qu'on risque de passer du temps à comprendre la complexité des arbres. Cependant, on a affaire ici à une bonne conception, et les autres solutions sont en général bien moins bonnes.

Tables

Comme les arbres, les tables en Swing sont vastes et puissantes. Elles sont destinées principalement à être la populaire grille d'interface avec les bases de données via la Connectivité Bases de Données Java : Java DataBase Connectivity (JDBC, présenté dans le Chapitre 15) et pour cela elles ont une flexibilité énorme, que l'on paie en complexité. Il y a ici suffisamment pour servir de base à un tableur complet et pourrait probablement être le sujet d'un livre complet. Cependant, il est également possible de créer une name="Index1768">JTablerelativement simple si on en comprend les bases.

La JTable contrôle la façon dont les données sont affichées, tandis que le TableModel contrôle les données elles-mêmes. Donc pour créer une JTable on créera d'abord un TableModel. On peut implémenter complètement l'interface TableModel, mais il est souvent plus simple d'hériter de la classe utilitaire AbstractTableModel :

//: c13:Table.java
// Démonstration simple d'une JTable.
// <applet code=Table
//  width=350 height=200></applet>
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.table.*;
importjavax.swing.event.*;
importcom.bruceeckel.swing.*;

publicclassTable extendsJApplet {
JTextArea txt = newJTextArea(4, 20);
// Le TableModel contrôle toutes les données :
classDataModel extendsAbstractTableModel {
   Object[][] data = {
     {"one", "two", "three", "four"},
     {"five", "six", "seven", "eight"},
     {"nine", "ten", "eleven", "twelve"},
   };
   // Imprime les données lorsque la table change :
   classTML implementsTableModelListener {
     publicvoidtableChanged(TableModelEvent e){
       txt.setText(""); // Vider le texte
       for(inti = 0; i          for(intj = 0; j            txt.append(data[i][j] + " ");
         txt.append("\n");
       }
     }
   }
   publicDataModel() {
     addTableModelListener(newTML());
   }
   publicintgetColumnCount() {
     returndata[0].length;
   }
   publicintgetRowCount() {
     returndata.length;
   }
   publicObject getValueAt(introw, intcol) {
     returndata[row][col];
   }
   publicvoid
   setValueAt(Object val, introw, intcol) {
     data[row][col] = val;
     // Indique que le changement a eu lieu :
     fireTableDataChanged();
   }
   publicboolean
   isCellEditable(introw, intcol) {
     returntrue;
   }
}
publicvoidinit() {
   Container cp = getContentPane();
   JTable table = newJTable(newDataModel());
   cp.add(newJScrollPane(table));
   cp.add(BorderLayout.SOUTH, txt);
}
publicstaticvoidmain(String[] args) {
   Console.run(newTable(), 350, 200);
}
} ///:~

DataModelcontient un tableau de données, mais on pourrait aussi obtenir les données depuis une autre source telle qu'une base de données. Le constructeur ajoute un TableModelListener qui imprime le tableau chaque fois que la table est modifiée. Les autres méthodes suivent les conventions de nommage des Beans ; elles sont utilisées par la JTable lorsqu'elle veut présenter les informations contenues dans DataModel. AbstractTableModel fournit des méthodes par défaut pour setValueAt() et isCellEditable() qui interdisent les modifications de données, de sorte que ces méthodes devront être redéfinies si on veut pouvoir modifier les données.

Une fois obtenu un TableModel, il suffit de le passer au constructeur de la JTable. Tous les détails concernant l'affichage, les modifications et la mise à jour seront automatiquement gérés. Cet exemple place également la JTable dans un JScrollPane.

Sélection de l'aspect de l'interface [Look & Feel]

Un des aspects très intéressants de Swing est le name="Index1770">Pluggable Look & Feel. Il permet à un programme d'émuler le look and feel de divers environnements d'exploitation. On peut même faire toutes sortes de choses comme par exemple changer le look and feel pendant l'exécution du programme. Toutefois, en général on désire soit sélectionner le look and feel toutes plateformes (qui est le Metal de Swing), soit sélectionner le look and feel du système courant, de sorte à donner l'impression que le programme Java a été créé spécifiquement pour ce système. Le code permettant de sélectionner chacun de ces comportements est assez simple, mais il faut l'exécuter avant de créer les composants visuels, car ceux-ci seront créés selon le look and feel courant et ne seront pas changés par le simple changement de look and feel au milieu du programme (ce processus est compliqué et peu banal, et nous en laisserons le développement à des livres spécifiques sur Swing).

En fait, si on veut utiliser le look and feel toutes plateformes (metal) qui est la caractéristique des programmes Swing, il n'y a rien de particulier à faire, c'est la valeur par défaut. Si au contraire on veut utiliser le look and feel de l'environnement d'exploitation courant, il suffit d'insérer le code suivant, normalement au début du main() mais de toutes façons avant d'ajouter des composants :

try{
UIManager.setLookAndFeel(UIManager.
   getSystemLookAndFeelClassName());
} catch(Exception e) {}

Il n'y a pas besoin de faire quoi que ce soit dans la clause catch car le UIManager se positionnera par défaut au look and feel toutes plateformes si votre tentative d'installation d'un des autres échoue. Toutefois, pour le debug, l'exception peut être utile, de sorte qu'on peut au minimum placer une instruction d'impression dans la clause catch.

Voici un programme qui utilise un argument de ligne de commande pour sélectionner un look and feel, et montre à quoi ressemblent différents composants dans le look and feel choisi :

//: c13:LookAndFeel.java
// Sélection de divers looks & feels.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
importjava.util.*;
importcom.bruceeckel.swing.*;

publicclassLookAndFeel extendsJFrame {
String[] choices = {
   "eeny", "meeny", "minie", "moe", "toe", "you"
};
Component[] samples = {
   newJButton("JButton"),
   newJTextField("JTextField"),
   newJLabel("JLabel"),
   newJCheckBox("JCheckBox"),
   newJRadioButton("Radio"),
   newJComboBox(choices),
   newJList(choices),
};
publicLookAndFeel() {
   super("Look And Feel");
   Container cp = getContentPane();
   cp.setLayout(newFlowLayout());
   for(inti = 0; i      cp.add(samples[i]);
}
privatestaticvoidusageError() {
   System.out.println(
     "Usage:LookAndFeel [cross|system|motif]");
   System.exit(1);
}
publicstaticvoidmain(String[] args) {
   if(args.length == 0) usageError();
   if(args[0].equals("cross")) {
     try{
       UIManager.setLookAndFeel(UIManager.
         getCrossPlatformLookAndFeelClassName());
     } catch(Exception e) {
         e.printStackTrace(System.err);
     }
   } elseif(args[0].equals("system")) {
     try{
       UIManager.setLookAndFeel(UIManager.
         getSystemLookAndFeelClassName());
     } catch(Exception e) {
         e.printStackTrace(System.err);
     }
   } elseif(args[0].equals("motif")) {
     try{
       UIManager.setLookAndFeel("com.sun.java."+
         "swing.plaf.motif.MotifLookAndFeel");
     } catch(Exception e) {
         e.printStackTrace(System.err);
     }
   } elseusageError();
   // Remarquons que le look & feel doit être positionné
   // avant la création des composants.
   Console.run(newLookAndFeel(), 300, 200);
}
} ///:~

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